package com.nightonke.boommenu;

import com.nightonke.boommenu.Animation.*;
import ohos.aafwk.ability.Ability;
import ohos.agp.animation.Animator;
import ohos.agp.animation.Animator.TimelineCurve;
import ohos.agp.animation.AnimatorProperty;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.app.Context;
import ohos.agp.components.element.StateElement;
import ohos.app.dispatcher.TaskDispatcher;
import ohos.app.dispatcher.task.TaskPriority;
import ohos.agp.render.Canvas;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.bundle.AbilityInfo.DisplayOrientation;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.global.resource.NotExistException;
import ohos.global.resource.ResourceManager;
import ohos.global.resource.WrongTypeException;
import ohos.multimodalinput.event.KeyEvent;
import ohos.multimodalinput.event.TouchEvent;
import ohos.powermanager.PowerManager;

import com.nightonke.boommenu.BoomButtons.BoomButton;
import com.nightonke.boommenu.BoomButtons.BoomButtonBuilder;
import com.nightonke.boommenu.BoomButtons.ButtonPlaceAlignmentEnum;
import com.nightonke.boommenu.BoomButtons.ButtonPlaceEnum;
import com.nightonke.boommenu.BoomButtons.ButtonPlaceManager;
import com.nightonke.boommenu.BoomButtons.HamButton;
import com.nightonke.boommenu.BoomButtons.InnerOnBoomButtonClickListener;
import com.nightonke.boommenu.BoomButtons.SimpleCircleButton;
import com.nightonke.boommenu.BoomButtons.TextInsideCircleButton;
import com.nightonke.boommenu.BoomButtons.TextOutsideCircleButton;
import com.nightonke.boommenu.Piece.BoomPiece;
import com.nightonke.boommenu.Piece.PiecePlaceEnum;
import com.nightonke.boommenu.Piece.PiecePlaceManager;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

import static com.nightonke.boommenu.Piece.PiecePlaceEnum.Share;

/**
 * Created by Weiping Huang at 14:33 on 16/11/6
 * For Personal Open Source
 * Contact me at 2584541288@qq.com or nightonke@outlook.com
 */

@SuppressWarnings("unused")
// Todo Landscape
public class BoomMenuButton extends StackLayout implements InnerOnBoomButtonClickListener {

    //region Fields
    protected static final String TAG = "BoomMenuButton";
    // String
    private static final String BMB_CACHE_OPTIMIZATION = "bmb_cacheOptimization";
    private static final String BMB_BOOM_IN_WHOLE_SCREEN = "bmb_boomInWholeScreen";
    private static final String BMB_IN_LIST = "bmb_inList";
    private static final String BMB_IN_FRAGMENT = "bmb_inFragment";
    private static final String BMB_BACK_PRESS_LISTENED = "bmb_backPressListened";
    private static final String BMB_ORIENTATION_ADAPTABLE = "bmb_orientationAdaptable";
    private static final String BMB_SHADOW_EFFECT = "bmb_shadowEffect";
    private static final String BMB_SHADOW_RADIUS = "bmb_shadowRadius";
    private static final String BMB_SHADOW_COLOR = "bmb_shadowColor";
    private static final String BMB_SHADOW_OFFSET_X = "bmb_shadowOffsetX";
    private static final String BMB_SHADOW_OFFSET_Y = "bmb_shadowOffsetY";
    private static final String BMB_BUTTON_RADIUS = "bmb_buttonRadius";
    private static final String BMB_BUTTON_ENUM = "bmb_buttonEnum";
    private static final String BMB_BACKGROUND_EFFECT = "bmb_backgroundEffect";
    private static final String BMB_RIPPLE_EFFECT = "bmb_rippleEffect";
    private static final String BMB_NORMAL_COLOR = "bmb_normalColor";
    private static final String BMB_HIGHLIGHTED_COLOR = "bmb_highlightedColor";
    private static final String BMB_UNABLE_COLOR = "bmb_unableColor";
    private static final String BMB_DRAGGABLE = "bmb_draggable";
    private static final String BMB_EDGE_INSETS_LEFT = "bmb_edgeInsetsLeft";
    private static final String BMB_EDGE_INSETS_TOP = "bmb_edgeInsetsTop";
    private static final String BMB_EDGE_INSETS_RIGHT = "bmb_edgeInsetsRight";
    private static final String BMB_EDGE_INSETS_BOTTOM = "bmb_edgeInsetsBottom";
    private static final String BMB_DOT_RADIUS = "bmb_dotRadius";
    private static final String BMB_HAM_WIDTH = "bmb_hamWidth";
    private static final String BMB_HAM_HEIGHT = "bmb_hamHeight";
    private static final String BMB_PIECE_CORNER_RADIUS = "bmb_pieceCornerRadius";
    private static final String BMB_PIECE_HORIZONTAL_MARGIN = "bmb_pieceHorizontalMargin";
    private static final String BMB_PIECE_VERTICAL_MARGIN = "bmb_pieceVerticalMargin";
    private static final String BMB_PIECE_INCLINED_MARGIN = "bmb_pieceInclinedMargin";
    private static final String BMB_PIECE_PLACE_ENUM = "bmb_piecePlaceEnum";
    private static final String BMB_SHARED_LINE_LENGTH = "bmb_sharedLineLength";
    private static final String BMB_SHARE_LINE1_COLOR = "bmb_shareLine1Color";
    private static final String BMB_SHARE_LINE2_COLOR = "bmb_shareLine2Color";
    private static final String BMB_SHARE_LINE_WIDTH = "bmb_shareLineWidth";
    private static final String BMB_DIM_COLOR = "bmb_dimColor";
    private static final String BMB_SHOW_DURATION = "bmb_showDuration";
    private static final String BMB_SHOW_DELAY = "bmb_showDelay";
    private static final String BMB_HIDE_DURATION = "bmb_hideDuration";
    private static final String BMB_HIDE_DELAY = "bmb_hideDelay";
    private static final String BMB_CANCELABLE = "bmb_cancelable";
    private static final String BMB_AUTO_HIDE = "bmb_autoHide";
    private static final String BMB_ORDER_ENUM = "bmb_orderEnum";
    private static final String BMB_FRAMES = "bmb_frames";
    private static final String BMB_BOOM_ENUM = "bmb_boomEnum";
    private static final String BMB_SHOW_MOVE_EASE_ENUM = "bmb_showMoveEaseEnum";
    private static final String BMB_SHOW_SCALE_EASE_ENUM = "bmb_showScaleEaseEnum";
    private static final String BMB_SHOW_ROTATE_EASE_ENUM = "bmb_showRotateEaseEnum";
    private static final String BMB_HIDE_MOVE_EASE_ENUM = "bmb_hideMoveEaseEnum";
    private static final String BMB_HIDE_SCALE_EASE_ENUM = "bmb_hideScaleEaseEnum";
    private static final String BMB_HIDE_ROTATE_EASE_ENUM = "bmb_hideRotateEaseEnum";
    private static final String BMB_ROTATE_DEGREE = "bmb_rotateDegree";
    private static final String BMB_USE_3D_TRANSFORM_ANIMATION = "bmb_use3DTransformAnimation";
    private static final String BMB_AUTO_BOOM = "bmb_autoBoom";
    private static final String BMB_AUTO_BOOM_IMMEDIATELY = "bmb_autoBoomImmediately";
    private static final String BMB_BUTTON_PLACE_ENUM = "bmb_buttonPlaceEnum";
    private static final String BMB_BUTTON_PLACE_ALIGNMENT_ENUM = "bmb_buttonPlaceAlignmentEnum";
    private static final String BMB_BUTTON_HORIZONTAL_MARGIN = "bmb_buttonHorizontalMargin";
    private static final String BMB_BUTTON_VERTICAL_MARGIN = "bmb_buttonVerticalMargin";
    private static final String BMB_BUTTON_INCLINED_MARGIN = "bmb_buttonInclinedMargin";
    private static final String BMB_BUTTON_TOP_MARGIN = "bmb_buttonTopMargin";
    private static final String BMB_BUTTON_BOTTOM_MARGIN = "bmb_buttonBottomMargin";
    private static final String BMB_BUTTON_LEFT_MARGIN = "bmb_buttonLeftMargin";
    private static final String BMB_BUTTON_RIGHT_MARGIN = "bmb_buttonRightMargin";
    private static final String BMB_BOTTOM_HAM_BUTTON_TOP_MARGIN = "bmb_bottomHamButtonTopMargin";

    // Basic
    private Context context;
    private boolean needToLayout = true;
    private boolean cacheOptimization;
    private boolean boomInWholeScreen;
    private boolean inList;
    private boolean inFragment;
    private boolean isBackPressListened = true;
    private Runnable layoutJobsRunnable;

    // Shadow
    private boolean shadowEffect;
    private int shadowOffsetX;
    private int shadowOffsetY;
    private int shadowRadius;
    private Color shadowColor;
    private BMBShadow shadow;

    // Button
    private int buttonRadius;
    private ButtonEnum buttonEnum = ButtonEnum.Unknown;
    private boolean backgroundEffect;
    private boolean rippleEffect;
    private Color normalColor;
    private Color highlightedColor;
    private Color unableColor;
    private boolean draggable;
    private float startPositionX;
    private float startPositionY;
    private boolean ableToStartDragging = false;
    private boolean isDragging = false;
    private float lastMotionX = -1;
    private float lastMotionY = -1;
    private Rect edgeInsetsInParentView;
    private StackLayout button;

    // Piece
    private ArrayList<BoomPiece> pieces;
    private ArrayList<RectFloat> piecePositions;
    private float dotRadius;
    private float hamWidth;
    private float hamHeight;
    private float pieceCornerRadius = -1;
    private float pieceHorizontalMargin;
    private float pieceVerticalMargin;
    private float pieceInclinedMargin;
    private float shareLineLength;
    private Color shareLine1Color;
    private Color shareLine2Color;
    private float shareLineWidth;
    private ShareLinesView shareLinesView;
    private PiecePlaceEnum piecePlaceEnum = PiecePlaceEnum.Unknown;
    private ArrayList<Point> customPiecePlacePositions = new ArrayList<>();

    // Animation
    private int animatingViewNumber = 0;
    private OnBoomListener onBoomListener;
    private Color dimColor;
    private long showDuration;
    private long showDelay;
    private long hideDuration;
    private long hideDelay;
    private boolean cancelable;
    private boolean autoHide;
    private OrderEnum orderEnum;
    private int frames;
    private BoomEnum boomEnum;
    private EaseEnum showMoveEaseEnum;
    private EaseEnum showScaleEaseEnum;
    private EaseEnum showRotateEaseEnum;
    private EaseEnum hideMoveEaseEnum;
    private EaseEnum hideScaleEaseEnum;
    private EaseEnum hideRotateEaseEnum;
    private int rotateDegree;
    private boolean use3DTransformAnimation;
    private boolean autoBoom;
    private boolean autoBoomImmediately;
    private BoomStateEnum boomStateEnum = BoomStateEnum.DidReboom;

    // Background
    private BackgroundView background;

    // Boom Buttons
    private ArrayList<BoomButton> boomButtons = new ArrayList<>();
    private ArrayList<BoomButtonBuilder> boomButtonBuilders = new ArrayList<>();
    private float simpleCircleButtonRadius;
    private float textInsideCircleButtonRadius;
    private float textOutsideCircleButtonWidth;
    private float textOutsideCircleButtonHeight;
    private float hamButtonWidth;
    private float hamButtonHeight;
    private ButtonPlaceEnum buttonPlaceEnum = ButtonPlaceEnum.Unknown;
    private ArrayList<Point> customButtonPlacePositions = new ArrayList<>();
    private ButtonPlaceAlignmentEnum buttonPlaceAlignmentEnum;
    private float buttonHorizontalMargin;
    private float buttonVerticalMargin;
    private float buttonInclinedMargin;
    private float buttonTopMargin;
    private float buttonBottomMargin;
    private float buttonLeftMargin;
    private float buttonRightMargin;
    private ArrayList<Point> startPositions;
    private ArrayList<Point> endPositions;
    private float bottomHamButtonTopMargin;
    private boolean needToCreateButtons = true;
    private boolean needToCalculateStartPositions = true;
    private int lastReboomIndex = -1;

    // Orientation
    private boolean isOrientationAdaptable;
    private DisplayOrientation lastOrientation = DisplayOrientation.UNSPECIFIED;
    private boolean isOrientationChanged = false;
    private ComponentContainer rootContainer;

    //endregion

    private void ____________________________Initialization() {}

    public BoomMenuButton(Context contextIn) {
        super(contextIn);
        init(contextIn, null);
    }

    public BoomMenuButton(Context contextIn, AttrSet attrs) {
        super(contextIn, attrs);
        init(contextIn, attrs);
    }

    public BoomMenuButton(Context contextIn, AttrSet attrs, String defStyleAttr) {
        super(contextIn, attrs, defStyleAttr);
        init(contextIn, attrs);
    }

    private void init(Context contextIn, AttrSet attrs) {
        this.context = contextIn;
        LayoutScatter.getInstance(contextIn).parse(ResourceTable.Layout_bmb, this, true);
        initAttrs(contextIn, attrs);
        initShadow();

        initButton();

        addDrawTask(new DrawTask() {
            @Override
            public void onDraw(Component component, Canvas canvas) {
                checkAutoBoom();
                invalidate();
            }
        });
        this.setBindStateChangedListener(new BindStateChangedListener() {
            @Override
            public void onComponentBoundToWindow(Component component) {
                if (isOrientationAdaptable) initOrientationListener();
                postLayout();
            }

            @Override
            public void onComponentUnboundFromWindow(Component component) {
            }
        });
        this.setArrangeListener(new ArrangeListener() {
            @Override
            public boolean onArrange(int left, int top, int width, int height) {
                if(width > 0 && height > 0) {
                    onLayout();
                }
                return false;
            }
        });
    }
    private Color getColorFromAttrs(String attrValue, Attr attr) {
        Color ret;
        if (attrValue.contains(":")) {
            ret = new Color(Integer.parseInt(attrValue.substring(attrValue.indexOf(":")+1)));
        } else {
            ret = attr.getColorValue();
        }
        return ret;
    }

    private void attrsLayout(Context contextIn, AttrSet attrSet) {
        this.context = contextIn;

        for (int i = 0; i < attrSet.getLength(); i++) {
            Attr attr = attrSet.getAttr(i).get();
            String attrName = attr.getName();
            String attrValue = attr.getStringValue();
            switch (attrName) {
                case BMB_CACHE_OPTIMIZATION:
                    if(attrValue != null && !"".equals(attrValue)){
                        cacheOptimization = attr.getBoolValue();
                    }
                    break;
                case BMB_BOOM_IN_WHOLE_SCREEN:
                    if(attrValue != null && !"".equals(attrValue)){
                        boomInWholeScreen = attr.getBoolValue();
                    }
                    break;
                case BMB_IN_LIST:
                    if(attrValue != null && !"".equals(attrValue)){
                        inList = attr.getBoolValue();
                    }
                    break;
                case BMB_IN_FRAGMENT:
                    if(attrValue != null && !"".equals(attrValue)){
                        inFragment = attr.getBoolValue();
                    }
                    break;
                case BMB_BACK_PRESS_LISTENED:
                    if(attrValue != null && !"".equals(attrValue)){
                        isBackPressListened = attr.getBoolValue();
                    }
                    break;
                case BMB_ORIENTATION_ADAPTABLE:
                    if(attrValue != null && !"".equals(attrValue)){
                        isOrientationAdaptable = attr.getBoolValue();
                    }
                    break;
                case BMB_SHADOW_EFFECT:
                    if(attrValue != null && !"".equals(attrValue)){
                        shadowEffect = attr.getBoolValue();
                    }
                    break;
                case BMB_SHADOW_RADIUS:
                    if(attrValue != null && !"".equals(attrValue)){
                        shadowRadius = attr.getIntegerValue();
                    }
                    break;
                case BMB_SHADOW_COLOR:
                    if(attrValue != null && !"".equals(attrValue)){
                        shadowColor = getColorFromAttrs(attrValue, attr);//attr.getColorValue();
                    }
                    break;
                case BMB_SHADOW_OFFSET_X:
                    if(attrValue != null && !"".equals(attrValue)){
                        shadowOffsetX = attr.getIntegerValue();
                    }
                    break;
                case BMB_SHADOW_OFFSET_Y:
                    if(attrValue != null && !"".equals(attrValue)){
                        shadowOffsetY = attr.getIntegerValue();
                    }
                    break;
                case BMB_BUTTON_RADIUS:
                    if(attrValue != null && !"".equals(attrValue)){
                        buttonRadius = attr.getIntegerValue();
                    }
                    break;
                case BMB_BUTTON_ENUM:
                    if(attrValue != null && !"".equals(attrValue)){
                        switch(attrValue) {
                            case "simpleCircle":
                                buttonEnum = ButtonEnum.getEnum(0);
                                break;
                            case "textInsideCircle":
                                buttonEnum =ButtonEnum.getEnum(1);
                                break;
                            case "textOutsideCircle":
                                buttonEnum =ButtonEnum.getEnum(2);
                                break;
                            case "ham":
                                buttonEnum =ButtonEnum.getEnum(3);
                                break;
                        }
                    }
                    break;
                case BMB_BACKGROUND_EFFECT:
                    if(attrValue != null && !"".equals(attrValue)){
                        backgroundEffect = attr.getBoolValue();
                    }
                    break;
                case BMB_RIPPLE_EFFECT:
                    if(attrValue != null && !"".equals(attrValue)){
                        rippleEffect = attr.getBoolValue();
                    }
                    break;
                case BMB_NORMAL_COLOR:
                    if(attrValue != null && !"".equals(attrValue)){
                        normalColor = getColorFromAttrs(attrValue, attr);//attr.getColorValue();
                    }
                    break;
                case BMB_HIGHLIGHTED_COLOR:
                    if(attrValue != null && !"".equals(attrValue)){
                        highlightedColor = getColorFromAttrs(attrValue, attr);
//                        highlightedColor = attr.getColorValue();
                    }
                    break;

                case BMB_UNABLE_COLOR:
                    if(attrValue != null && !"".equals(attrValue)){
                        unableColor = getColorFromAttrs(attrValue, attr);//attr.getColorValue();
                    }
                    break;
                case BMB_DRAGGABLE:
                    if(attrValue != null && !"".equals(attrValue)){
                        draggable = attr.getBoolValue();
                    }
                    break;
                case BMB_EDGE_INSETS_LEFT:
                    if(attrValue != null && !"".equals(attrValue)){
                        edgeInsetsInParentView.left = attr.getIntegerValue();
                    }
                    break;
                case BMB_EDGE_INSETS_TOP:
                    if(attrValue != null && !"".equals(attrValue)){
                        edgeInsetsInParentView.top = attr.getIntegerValue();
                    }
                    break;
                case BMB_EDGE_INSETS_RIGHT:
                    if(attrValue != null && !"".equals(attrValue)){
                        edgeInsetsInParentView.right = attr.getIntegerValue();
                    }
                    break;
                case BMB_EDGE_INSETS_BOTTOM:
                    if(attrValue != null && !"".equals(attrValue)){
                        edgeInsetsInParentView.bottom = attr.getIntegerValue();
                    }
                    break;
                case BMB_DOT_RADIUS:
                    if(attrValue != null && !"".equals(attrValue)){
                        dotRadius = attr.getFloatValue();
                    }
                    break;
                case BMB_HAM_WIDTH:
                    if(attrValue != null && !"".equals(attrValue)){
                        hamWidth = attr.getFloatValue();
                    }
                    break;
                case BMB_HAM_HEIGHT:
                    if(attrValue != null && !"".equals(attrValue)){
                        hamHeight = attr.getFloatValue();
                    }
                    break;
                case BMB_PIECE_CORNER_RADIUS:
                    if(attrValue != null && !"".equals(attrValue)){
                        pieceCornerRadius = attr.getFloatValue();
                    }
                    break;
                case BMB_PIECE_HORIZONTAL_MARGIN:
                    if(attrValue != null && !"".equals(attrValue)){
                        pieceHorizontalMargin = attr.getFloatValue();
                    }
                    break;
                case BMB_PIECE_VERTICAL_MARGIN:
                    if(attrValue != null && !"".equals(attrValue)){
                        pieceVerticalMargin = attr.getFloatValue();
                    }
                    break;
                case BMB_PIECE_INCLINED_MARGIN:
                    if(attrValue != null && !"".equals(attrValue)){
                        pieceInclinedMargin = attr.getFloatValue();
                    }
                    break;
                case BMB_PIECE_PLACE_ENUM:
                    if(attrValue != null && !"".equals(attrValue)){
                        switch(attrValue){
                            case "piecePlace_dot_1":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(0);
                                break;
                            case "piecePlace_dot_2_1":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(1);
                                break;
                            case "piecePlace_dot_2_2":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(2);
                                break;
                            case "piecePlace_dot_3_1":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(3);
                                break;
                            case "piecePlace_dot_3_2":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(4);
                                break;
                            case "piecePlace_dot_3_3":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(5);
                                break;
                            case "piecePlace_dot_3_4":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(6);
                                break;
                            case "piecePlace_dot_4_1":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(7);
                                break;
                            case "piecePlace_dot_4_2":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(8);
                                break;
                            case "piecePlace_dot_5_1":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(9);
                                break;
                            case "piecePlace_dot_5_2":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(10);
                                break;
                            case "piecePlace_dot_5_3":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(11);
                                break;
                            case "piecePlace_dot_5_4":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(12);
                                break;
                            case "piecePlace_dot_6_1":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(13);
                                break;
                            case "piecePlace_dot_6_2":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(14);
                                break;
                            case "piecePlace_dot_6_3":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(15);
                                break;
                            case "piecePlace_dot_6_4":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(16);
                                break;
                            case "piecePlace_dot_6_5":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(17);
                                break;
                            case "piecePlace_dot_6_6":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(18);
                                break;
                            case "piecePlace_dot_7_1":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(19);
                                break;
                            case "piecePlace_dot_7_2":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(20);
                                break;
                            case "piecePlace_dot_7_3":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(21);
                                break;
                            case "piecePlace_dot_7_4":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(22);
                                break;
                            case "piecePlace_dot_7_5":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(23);
                                break;
                            case "piecePlace_dot_7_6":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(24);
                                break;
                            case "piecePlace_dot_8_1":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(25);
                                break;
                            case "piecePlace_dot_8_2":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(26);
                                break;
                            case "piecePlace_dot_8_3":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(27);
                                break;
                            case "piecePlace_dot_8_4":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(28);
                                break;
                            case "piecePlace_dot_8_5":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(29);
                                break;
                            case "piecePlace_dot_8_6":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(30);
                                break;
                            case "piecePlace_dot_8_7":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(31);
                                break;
                            case "piecePlace_dot_9_1":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(32);
                                break;
                            case "piecePlace_dot_9_2":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(33);
                                break;
                            case "piecePlace_dot_9_3":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(34);
                                break;
                            case "piecePlace_ham_1":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(35);
                                break;
                            case "piecePlace_ham_2":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(36);
                                break;
                            case "piecePlace_ham_3":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(37);
                                break;
                            case "piecePlace_ham_4":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(38);
                                break;
                            case "piecePlace_ham_5":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(39);
                                break;
                            case "piecePlace_ham_6":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(40);
                                break;
                            case "piecePlace_share":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(41);
                                break;
                            case "piecePlace_custom":
                                piecePlaceEnum = PiecePlaceEnum.getEnum(42);
                                break;
                        }

                    }
                    break;
                case BMB_SHARED_LINE_LENGTH:
                    if(attrValue != null && !"".equals(attrValue)){
                        shareLineLength = attr.getFloatValue();
                    }
                    break;
                case BMB_SHARE_LINE1_COLOR:
                    if(attrValue != null && !"".equals(attrValue)){
                        shareLine1Color = getColorFromAttrs(attrValue, attr);//attr.getColorValue();
                    }
                    break;
                case BMB_SHARE_LINE2_COLOR:
                    if(attrValue != null && !"".equals(attrValue)){
                        shareLine2Color = getColorFromAttrs(attrValue, attr);//attr.getColorValue();
                    }
                    break;
                case BMB_SHARE_LINE_WIDTH:
                    if(attrValue != null && !"".equals(attrValue)){
                        shareLineWidth = attr.getFloatValue();
                    }
                    break;
                case BMB_DIM_COLOR:
                    if(attrValue != null && !"".equals(attrValue)){
                        dimColor = getColorFromAttrs(attrValue, attr);//attr.getColorValue();
                    }
                    break;
                case BMB_SHOW_DURATION:
                    if(attrValue != null && !"".equals(attrValue)){
                        showDuration = attr.getLongValue();
                    }
                    break;
                case BMB_SHOW_DELAY:
                    if(attrValue != null && !"".equals(attrValue)){
                        showDelay = attr.getLongValue();
                    }
                    break;
                case BMB_HIDE_DURATION:
                    if(attrValue != null && !"".equals(attrValue)){
                        hideDuration = attr.getLongValue();
                    }
                    break;
                case BMB_HIDE_DELAY:
                    if(attrValue != null && !"".equals(attrValue)){
                        hideDelay = attr.getLongValue();
                    }
                    break;
                case BMB_CANCELABLE:
                    if(attrValue != null && !"".equals(attrValue)){
                        cancelable = attr.getBoolValue();
                    }
                    break;
                case BMB_AUTO_HIDE:
                    if(attrValue != null && !"".equals(attrValue)){
                        autoHide = attr.getBoolValue();
                    }
                    break;
                case BMB_ORDER_ENUM:
                    if(attrValue != null && !"".equals(attrValue)){
                        switch(attrValue){
                            case "orderDefault":
                                orderEnum = OrderEnum.getEnum(0);
                                break;
                            case "orderReverse":
                                orderEnum = OrderEnum.getEnum(1);
                                break;
                            case "orderRandom":
                                orderEnum = OrderEnum.getEnum(2);
                                break;
                        }
                    }
                    break;
                case BMB_FRAMES:
                    if(attrValue != null && !"".equals(attrValue)){
                        frames = attr.getIntegerValue();
                    }
                    break;
                case BMB_BOOM_ENUM:
                    if(attrValue != null && !"".equals(attrValue)){
                        switch (attrValue){
                            case "boomLine":
                                boomEnum = BoomEnum.getEnum(0);
                                break;
                            case "boomParabola_1":
                                boomEnum = BoomEnum.getEnum(1);
                                break;
                            case "boomParabola_2":
                                boomEnum = BoomEnum.getEnum(2);
                                break;
                            case "boomParabola_3":
                                boomEnum = BoomEnum.getEnum(3);
                                break;
                            case "boomParabola_4":
                                boomEnum = BoomEnum.getEnum(4);
                                break;
                            case "boomHorizontalThrow_1":
                                boomEnum = BoomEnum.getEnum(5);
                                break;
                            case "boomHorizontalThrow_2":
                                boomEnum = BoomEnum.getEnum(6);
                                break;
                            case "boomRandom":
                                boomEnum = BoomEnum.getEnum(7);
                                break;
                        }
                    }
                    break;
                case BMB_SHOW_MOVE_EASE_ENUM:
                    if(attrValue != null && !"".equals(attrValue)){
                        switch (attrValue){
                            case "inSine":
                                showMoveEaseEnum = EaseEnum.getEnum(0);
                                break;
                            case "outSine":
                                showMoveEaseEnum = EaseEnum.getEnum(1);
                                break;
                            case "inOutSine":
                                showMoveEaseEnum = EaseEnum.getEnum(2);
                                break;
                            case "inQuad":
                                showMoveEaseEnum = EaseEnum.getEnum(3);
                                break;
                            case "outQuad":
                                showMoveEaseEnum = EaseEnum.getEnum(4);
                                break;
                            case "inOutQuad":
                                showMoveEaseEnum = EaseEnum.getEnum(5);
                                break;
                            case "inCubic":
                                showMoveEaseEnum = EaseEnum.getEnum(6);
                                break;
                            case "outCubic":
                                showMoveEaseEnum = EaseEnum.getEnum(7);
                                break;
                            case "inOutCubic":
                                showMoveEaseEnum = EaseEnum.getEnum(8);
                                break;
                            case "inQuart":
                                showMoveEaseEnum = EaseEnum.getEnum(9);
                                break;
                            case "outQuart":
                                showMoveEaseEnum = EaseEnum.getEnum(10);
                                break;
                            case "inOutQuart":
                                showMoveEaseEnum = EaseEnum.getEnum(11);
                                break;
                            case "inQuint":
                                showMoveEaseEnum = EaseEnum.getEnum(12);
                                break;
                            case "outQuint":
                                showMoveEaseEnum = EaseEnum.getEnum(13);
                                break;
                            case "inOutQuint":
                                showMoveEaseEnum = EaseEnum.getEnum(14);
                                break;
                            case "inExpo":
                                showMoveEaseEnum = EaseEnum.getEnum(15);
                                break;
                            case "outExpo":
                                showMoveEaseEnum = EaseEnum.getEnum(16);
                                break;
                            case "inOutExpo":
                                showMoveEaseEnum = EaseEnum.getEnum(17);
                                break;
                            case "inCirc":
                                showMoveEaseEnum = EaseEnum.getEnum(18);
                                break;
                            case "outCirc":
                                showMoveEaseEnum = EaseEnum.getEnum(19);
                                break;
                            case "inOutCirc":
                                showMoveEaseEnum = EaseEnum.getEnum(20);
                                break;
                            case "inBack":
                                showMoveEaseEnum = EaseEnum.getEnum(21);
                                break;
                            case "outBack":
                                showMoveEaseEnum = EaseEnum.getEnum(22);
                                break;
                            case "inOutBack":
                                showMoveEaseEnum = EaseEnum.getEnum(23);
                                break;
                            case "inElastic":
                                showMoveEaseEnum = EaseEnum.getEnum(24);
                                break;
                            case "outElastic":
                                showMoveEaseEnum = EaseEnum.getEnum(25);
                                break;
                            case "inOutElastic":
                                showMoveEaseEnum = EaseEnum.getEnum(26);
                                break;
                            case "inBounce":
                                showMoveEaseEnum = EaseEnum.getEnum(27);
                                break;
                            case "outBounce":
                                showMoveEaseEnum = EaseEnum.getEnum(28);
                                break;
                            case "inOutBounce":
                                showMoveEaseEnum = EaseEnum.getEnum(29);
                                break;
                            case "linear":
                                showMoveEaseEnum = EaseEnum.getEnum(30);
                                break;
                        }

                    }
                    break;
                case BMB_SHOW_SCALE_EASE_ENUM:
                    if(attrValue != null && !"".equals(attrValue)){
                        switch(attrValue){
                            case "inSine":
                                showScaleEaseEnum = EaseEnum.getEnum(0);
                                break;
                            case "outSine":
                                showScaleEaseEnum = EaseEnum.getEnum(1);
                                break;
                            case "inOutSine":
                                showScaleEaseEnum = EaseEnum.getEnum(2);
                                break;
                            case "inQuad":
                                showScaleEaseEnum = EaseEnum.getEnum(3);
                                break;
                            case "outQuad":
                                showScaleEaseEnum = EaseEnum.getEnum(4);
                                break;
                            case "inOutQuad":
                                showScaleEaseEnum = EaseEnum.getEnum(5);
                                break;
                            case "inCubic":
                                showScaleEaseEnum = EaseEnum.getEnum(6);
                                break;
                            case "outCubic":
                                showScaleEaseEnum = EaseEnum.getEnum(7);
                                break;
                            case "inOutCubic":
                                showScaleEaseEnum = EaseEnum.getEnum(8);
                                break;
                            case "inQuart":
                                showScaleEaseEnum = EaseEnum.getEnum(9);
                                break;
                            case "outQuart":
                                showMoveEaseEnum = EaseEnum.getEnum(10);
                                break;
                            case "inOutQuart":
                                showScaleEaseEnum = EaseEnum.getEnum(11);
                                break;
                            case "inQuint":
                                showScaleEaseEnum = EaseEnum.getEnum(12);
                                break;
                            case "outQuint":
                                showScaleEaseEnum = EaseEnum.getEnum(13);
                                break;
                            case "inOutQuint":
                                showScaleEaseEnum = EaseEnum.getEnum(14);
                                break;
                            case "inExpo":
                                showScaleEaseEnum = EaseEnum.getEnum(15);
                                break;
                            case "outExpo":
                                showScaleEaseEnum = EaseEnum.getEnum(16);
                                break;
                            case "inOutExpo":
                                showScaleEaseEnum = EaseEnum.getEnum(17);
                                break;
                            case "inCirc":
                                showScaleEaseEnum = EaseEnum.getEnum(18);
                                break;
                            case "outCirc":
                                showScaleEaseEnum = EaseEnum.getEnum(19);
                                break;
                            case "inOutCirc":
                                showScaleEaseEnum = EaseEnum.getEnum(20);
                                break;
                            case "inBack":
                                showScaleEaseEnum = EaseEnum.getEnum(21);
                                break;
                            case "outBack":
                                showScaleEaseEnum = EaseEnum.getEnum(22);
                                break;
                            case "inOutBack":
                                showScaleEaseEnum = EaseEnum.getEnum(23);
                                break;
                            case "inElastic":
                                showScaleEaseEnum = EaseEnum.getEnum(24);
                                break;
                            case "outElastic":
                                showScaleEaseEnum = EaseEnum.getEnum(25);
                                break;
                            case "inOutElastic":
                                showScaleEaseEnum = EaseEnum.getEnum(26);
                                break;
                            case "inBounce":
                                showScaleEaseEnum = EaseEnum.getEnum(27);
                                break;
                            case "outBounce":
                                showScaleEaseEnum = EaseEnum.getEnum(28);
                                break;
                            case "inOutBounce":
                                showScaleEaseEnum = EaseEnum.getEnum(29);
                                break;
                            case "linear":
                                showScaleEaseEnum = EaseEnum.getEnum(30);
                                break;
                        }

                    }
                    break;
                case BMB_SHOW_ROTATE_EASE_ENUM:
                    if(attrValue != null && !"".equals(attrValue)){
                        switch(attrValue){
                            case "inSine":
                                showRotateEaseEnum = EaseEnum.getEnum(0);
                                break;
                            case "outSine":
                                showRotateEaseEnum = EaseEnum.getEnum(1);
                                break;
                            case "inOutSine":
                                showRotateEaseEnum = EaseEnum.getEnum(2);
                                break;
                            case "inQuad":
                                showRotateEaseEnum = EaseEnum.getEnum(3);
                                break;
                            case "outQuad":
                                showRotateEaseEnum = EaseEnum.getEnum(4);
                                break;
                            case "inOutQuad":
                                showScaleEaseEnum = EaseEnum.getEnum(5);
                                break;
                            case "inCubic":
                                showRotateEaseEnum = EaseEnum.getEnum(6);
                                break;
                            case "outCubic":
                                showRotateEaseEnum = EaseEnum.getEnum(7);
                                break;
                            case "inOutCubic":
                                showRotateEaseEnum = EaseEnum.getEnum(8);
                                break;
                            case "inQuart":
                                showRotateEaseEnum = EaseEnum.getEnum(9);
                                break;
                            case "outQuart":
                                showRotateEaseEnum = EaseEnum.getEnum(10);
                                break;
                            case "inOutQuart":
                                showRotateEaseEnum = EaseEnum.getEnum(11);
                                break;
                            case "inQuint":
                                showRotateEaseEnum = EaseEnum.getEnum(12);
                                break;
                            case "outQuint":
                                showRotateEaseEnum = EaseEnum.getEnum(13);
                                break;
                            case "inOutQuint":
                                showRotateEaseEnum = EaseEnum.getEnum(14);
                                break;
                            case "inExpo":
                                showRotateEaseEnum = EaseEnum.getEnum(15);
                                break;
                            case "outExpo":
                                showRotateEaseEnum = EaseEnum.getEnum(16);
                                break;
                            case "inOutExpo":
                                showRotateEaseEnum = EaseEnum.getEnum(17);
                                break;
                            case "inCirc":
                                showRotateEaseEnum = EaseEnum.getEnum(18);
                                break;
                            case "outCirc":
                                showRotateEaseEnum = EaseEnum.getEnum(19);
                                break;
                            case "inOutCirc":
                                showRotateEaseEnum = EaseEnum.getEnum(20);
                                break;
                            case "inBack":
                                showRotateEaseEnum = EaseEnum.getEnum(21);
                                break;
                            case "outBack":
                                showRotateEaseEnum = EaseEnum.getEnum(22);
                                break;
                            case "inOutBack":
                                showRotateEaseEnum = EaseEnum.getEnum(23);
                                break;
                            case "inElastic":
                                showRotateEaseEnum = EaseEnum.getEnum(24);
                                break;
                            case "outElastic":
                                showRotateEaseEnum = EaseEnum.getEnum(25);
                                break;
                            case "inOutElastic":
                                showRotateEaseEnum = EaseEnum.getEnum(26);
                                break;
                            case "inBounce":
                                showRotateEaseEnum = EaseEnum.getEnum(27);
                                break;
                            case "outBounce":
                                showRotateEaseEnum = EaseEnum.getEnum(28);
                                break;
                            case "inOutBounce":
                                showRotateEaseEnum = EaseEnum.getEnum(29);
                                break;
                            case "linear":
                                showRotateEaseEnum = EaseEnum.getEnum(30);
                                break;
                        }
                    }
                    break;
                case BMB_HIDE_MOVE_EASE_ENUM:
                    if(attrValue != null && !"".equals(attrValue)){
                        switch(attrValue){
                            case "inSine":
                                hideMoveEaseEnum = EaseEnum.getEnum(0);
                                break;
                            case "outSine":
                                hideMoveEaseEnum = EaseEnum.getEnum(1);
                                break;
                            case "inOutSine":
                                hideMoveEaseEnum = EaseEnum.getEnum(2);
                                break;
                            case "inQuad":
                                hideMoveEaseEnum = EaseEnum.getEnum(3);
                                break;
                            case "outQuad":
                                hideMoveEaseEnum = EaseEnum.getEnum(4);
                                break;
                            case "inOutQuad":
                                hideMoveEaseEnum = EaseEnum.getEnum(5);
                                break;
                            case "inCubic":
                                hideMoveEaseEnum = EaseEnum.getEnum(6);
                                break;
                            case "outCubic":
                                hideMoveEaseEnum = EaseEnum.getEnum(7);
                                break;
                            case "inOutCubic":
                                hideMoveEaseEnum = EaseEnum.getEnum(8);
                                break;
                            case "inQuart":
                                hideMoveEaseEnum = EaseEnum.getEnum(9);
                                break;
                            case "outQuart":
                                hideMoveEaseEnum = EaseEnum.getEnum(10);
                                break;
                            case "inOutQuart":
                                hideMoveEaseEnum = EaseEnum.getEnum(11);
                                break;
                            case "inQuint":
                                hideMoveEaseEnum = EaseEnum.getEnum(12);
                                break;
                            case "outQuint":
                                hideMoveEaseEnum = EaseEnum.getEnum(13);
                                break;
                            case "inOutQuint":
                                hideMoveEaseEnum = EaseEnum.getEnum(14);
                                break;
                            case "inExpo":
                                hideMoveEaseEnum = EaseEnum.getEnum(15);
                                break;
                            case "outExpo":
                                hideMoveEaseEnum = EaseEnum.getEnum(16);
                                break;
                            case "inOutExpo":
                                hideMoveEaseEnum = EaseEnum.getEnum(17);
                                break;
                            case "inCirc":
                                hideMoveEaseEnum = EaseEnum.getEnum(18);
                                break;
                            case "outCirc":
                                hideMoveEaseEnum = EaseEnum.getEnum(19);
                                break;
                            case "inOutCirc":
                                hideMoveEaseEnum = EaseEnum.getEnum(20);
                                break;
                            case "inBack":
                                hideMoveEaseEnum = EaseEnum.getEnum(21);
                                break;
                            case "outBack":
                                hideMoveEaseEnum = EaseEnum.getEnum(22);
                                break;
                            case "inOutBack":
                                hideMoveEaseEnum = EaseEnum.getEnum(23);
                                break;
                            case "inElastic":
                                hideMoveEaseEnum = EaseEnum.getEnum(24);
                                break;
                            case "outElastic":
                                hideMoveEaseEnum = EaseEnum.getEnum(25);
                                break;
                            case "inOutElastic":
                                hideMoveEaseEnum = EaseEnum.getEnum(26);
                                break;
                            case "inBounce":
                                hideMoveEaseEnum = EaseEnum.getEnum(27);
                                break;
                            case "outBounce":
                                hideMoveEaseEnum = EaseEnum.getEnum(28);
                                break;
                            case "inOutBounce":
                                hideMoveEaseEnum = EaseEnum.getEnum(29);
                                break;
                            case "linear":
                                hideMoveEaseEnum = EaseEnum.getEnum(30);
                                break;
                        }
                    }
                    break;
                case BMB_HIDE_SCALE_EASE_ENUM:
                    if(attrValue != null && !"".equals(attrValue)){
                        switch(attrValue){
                            case "inSine":
                                hideScaleEaseEnum = EaseEnum.getEnum(0);
                                break;
                            case "outSine":
                                hideScaleEaseEnum = EaseEnum.getEnum(1);
                                break;
                            case "inOutSine":
                                hideScaleEaseEnum = EaseEnum.getEnum(2);
                                break;
                            case "inQuad":
                                hideScaleEaseEnum = EaseEnum.getEnum(3);
                                break;
                            case "outQuad":
                                hideScaleEaseEnum = EaseEnum.getEnum(4);
                                break;
                            case "inOutQuad":
                                hideScaleEaseEnum = EaseEnum.getEnum(5);
                                break;
                            case "inCubic":
                                hideScaleEaseEnum = EaseEnum.getEnum(6);
                                break;
                            case "outCubic":
                                hideScaleEaseEnum = EaseEnum.getEnum(7);
                                break;
                            case "inOutCubic":
                                hideScaleEaseEnum = EaseEnum.getEnum(8);
                                break;
                            case "inQuart":
                                hideScaleEaseEnum = EaseEnum.getEnum(9);
                                break;
                            case "outQuart":
                                hideScaleEaseEnum = EaseEnum.getEnum(10);
                                break;
                            case "inOutQuart":
                                hideScaleEaseEnum = EaseEnum.getEnum(11);
                                break;
                            case "inQuint":
                                hideScaleEaseEnum = EaseEnum.getEnum(12);
                                break;
                            case "outQuint":
                                hideScaleEaseEnum = EaseEnum.getEnum(13);
                                break;
                            case "inOutQuint":
                                hideScaleEaseEnum = EaseEnum.getEnum(14);
                                break;
                            case "inExpo":
                                hideScaleEaseEnum = EaseEnum.getEnum(15);
                                break;
                            case "outExpo":
                                hideScaleEaseEnum = EaseEnum.getEnum(16);
                                break;
                            case "inOutExpo":
                                hideMoveEaseEnum = EaseEnum.getEnum(17);
                                break;
                            case "inCirc":
                                hideScaleEaseEnum = EaseEnum.getEnum(18);
                                break;
                            case "outCirc":
                                hideScaleEaseEnum = EaseEnum.getEnum(19);
                                break;
                            case "inOutCirc":
                                hideScaleEaseEnum = EaseEnum.getEnum(20);
                                break;
                            case "inBack":
                                hideScaleEaseEnum = EaseEnum.getEnum(21);
                                break;
                            case "outBack":
                                hideScaleEaseEnum = EaseEnum.getEnum(22);
                                break;
                            case "inOutBack":
                                hideScaleEaseEnum = EaseEnum.getEnum(23);
                                break;
                            case "inElastic":
                                hideScaleEaseEnum = EaseEnum.getEnum(24);
                                break;
                            case "outElastic":
                                hideScaleEaseEnum = EaseEnum.getEnum(25);
                                break;
                            case "inOutElastic":
                                hideScaleEaseEnum = EaseEnum.getEnum(26);
                                break;
                            case "inBounce":
                                hideScaleEaseEnum = EaseEnum.getEnum(27);
                                break;
                            case "outBounce":
                                hideScaleEaseEnum = EaseEnum.getEnum(28);
                                break;
                            case "inOutBounce":
                                hideScaleEaseEnum = EaseEnum.getEnum(29);
                                break;
                            case "linear":
                                hideScaleEaseEnum = EaseEnum.getEnum(30);
                                break;
                        }
                    }
                    break;
                case BMB_HIDE_ROTATE_EASE_ENUM:
                    if(attrValue != null && !"".equals(attrValue)){
                        switch(attrValue){
                            case "inSine":
                                hideRotateEaseEnum = EaseEnum.getEnum(0);
                                break;
                            case "outSine":
                                hideRotateEaseEnum = EaseEnum.getEnum(1);
                                break;
                            case "inOutSine":
                                hideRotateEaseEnum = EaseEnum.getEnum(2);
                                break;
                            case "inQuad":
                                hideRotateEaseEnum = EaseEnum.getEnum(3);
                                break;
                            case "outQuad":
                                hideRotateEaseEnum = EaseEnum.getEnum(4);
                                break;
                            case "inOutQuad":
                                hideRotateEaseEnum = EaseEnum.getEnum(5);
                                break;
                            case "inCubic":
                                hideRotateEaseEnum = EaseEnum.getEnum(6);
                                break;
                            case "outCubic":
                                hideRotateEaseEnum = EaseEnum.getEnum(7);
                                break;
                            case "inOutCubic":
                                hideRotateEaseEnum = EaseEnum.getEnum(8);
                                break;
                            case "inQuart":
                                hideRotateEaseEnum = EaseEnum.getEnum(9);
                                break;
                            case "outQuart":
                                hideRotateEaseEnum = EaseEnum.getEnum(10);
                                break;
                            case "inOutQuart":
                                hideRotateEaseEnum = EaseEnum.getEnum(11);
                                break;
                            case "inQuint":
                                hideRotateEaseEnum = EaseEnum.getEnum(12);
                                break;
                            case "outQuint":
                                hideRotateEaseEnum = EaseEnum.getEnum(13);
                                break;
                            case "inOutQuint":
                                hideRotateEaseEnum = EaseEnum.getEnum(14);
                                break;
                            case "inExpo":
                                hideRotateEaseEnum = EaseEnum.getEnum(15);
                                break;
                            case "outExpo":
                                hideRotateEaseEnum = EaseEnum.getEnum(16);
                                break;
                            case "inOutExpo":
                                hideRotateEaseEnum = EaseEnum.getEnum(17);
                                break;
                            case "inCirc":
                                hideRotateEaseEnum = EaseEnum.getEnum(18);
                                break;
                            case "outCirc":
                                hideRotateEaseEnum = EaseEnum.getEnum(19);
                                break;
                            case "inOutCirc":
                                hideRotateEaseEnum = EaseEnum.getEnum(20);
                                break;
                            case "inBack":
                                hideRotateEaseEnum = EaseEnum.getEnum(21);
                                break;
                            case "outBack":
                                hideRotateEaseEnum = EaseEnum.getEnum(22);
                                break;
                            case "inOutBack":
                                hideRotateEaseEnum = EaseEnum.getEnum(23);
                                break;
                            case "inElastic":
                                hideRotateEaseEnum = EaseEnum.getEnum(24);
                                break;
                            case "outElastic":
                                hideRotateEaseEnum = EaseEnum.getEnum(25);
                                break;
                            case "inOutElastic":
                                hideRotateEaseEnum = EaseEnum.getEnum(26);
                                break;
                            case "inBounce":
                                hideRotateEaseEnum = EaseEnum.getEnum(27);
                                break;
                            case "outBounce":
                                hideRotateEaseEnum = EaseEnum.getEnum(28);
                                break;
                            case "inOutBounce":
                                hideRotateEaseEnum = EaseEnum.getEnum(29);
                                break;
                            case "linear":
                                hideRotateEaseEnum = EaseEnum.getEnum(30);
                                break;
                        }
                    }
                    break;
                case BMB_ROTATE_DEGREE:
                    if(attrValue != null && !"".equals(attrValue)){
                        rotateDegree = attr.getIntegerValue();
                    }
                    break;
                case BMB_USE_3D_TRANSFORM_ANIMATION:
                    if(attrValue != null && !"".equals(attrValue)){
                        use3DTransformAnimation = attr.getBoolValue();
                    }
                    break;
                case BMB_AUTO_BOOM:
                    if(attrValue != null && !"".equals(attrValue)){
                        autoBoom = attr.getBoolValue();
                    }
                    break;
                case BMB_AUTO_BOOM_IMMEDIATELY:
                    if(attrValue != null && !"".equals(attrValue)){
                        autoBoomImmediately = attr.getBoolValue();
                    }
                    break;
                case BMB_BUTTON_PLACE_ENUM:
                    if(attrValue != null && !"".equals(attrValue)){
                        switch(attrValue){
                            case "buttonPlace_sc_1":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(0);
                                break;
                            case "buttonPlace_sc_2_1":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(1);
                                break;
                            case "buttonPlace_sc_2_2":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(2);
                                break;
                            case "buttonPlace_sc_3_1":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(3);
                                break;
                            case "buttonPlace_sc_3_2":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(4);
                                break;
                            case "buttonPlace_sc_3_3":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(5);
                                break;
                            case "buttonPlace_sc_3_4":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(6);
                                break;
                            case "buttonPlace_sc_4_1":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(7);
                                break;
                            case "buttonPlace_sc_4_2":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(8);
                                break;
                            case "buttonPlace_sc_5_1":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(9);
                                break;
                            case "buttonPlace_sc_5_2":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(10);
                                break;
                            case "buttonPlace_sc_5_3":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(11);
                                break;
                            case "buttonPlace_sc_5_4":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(12);
                                break;
                            case "buttonPlace_sc_6_1":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(13);
                                break;
                            case "buttonPlace_sc_6_2":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(14);
                                break;
                            case "buttonPlace_sc_6_3":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(15);
                                break;
                            case "buttonPlace_sc_6_4":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(16);
                                break;
                            case "buttonPlace_sc_6_5":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(17);
                                break;
                            case "buttonPlace_sc_6_6":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(18);
                                break;
                            case "buttonPlace_sc_7_1":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(19);
                                break;
                            case "buttonPlace_sc_7_2":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(20);
                                break;
                            case "buttonPlace_sc_7_3":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(21);
                                break;
                            case "buttonPlace_sc_7_4":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(22);
                                break;
                            case "buttonPlace_sc_7_5":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(23);
                                break;
                            case "buttonPlace_sc_7_6":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(24);
                                break;
                            case "buttonPlace_sc_8_1":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(25);
                                break;
                            case "buttonPlace_sc_8_2":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(26);
                                break;
                            case "buttonPlace_sc_8_3":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(27);
                                break;
                            case "buttonPlace_sc_8_4":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(28);
                                break;
                            case "buttonPlace_sc_8_5":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(29);
                                break;
                            case "buttonPlace_sc_8_6":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(30);
                                break;
                            case "buttonPlace_sc_8_7":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(31);
                                break;
                            case "buttonPlace_sc_9_1":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(32);
                                break;
                            case "buttonPlace_sc_9_2":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(33);
                                break;
                            case "buttonPlace_sc_9_3":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(34);
                                break;
                            case "buttonPlace_ham_1":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(35);
                                break;
                            case "buttonPlace_ham_2":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(36);
                                break;
                            case "buttonPlace_ham_3":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(37);
                                break;
                            case "buttonPlace_ham_4":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(38);
                                break;
                            case "buttonPlace_ham_5":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(39);
                                break;
                            case "buttonPlace_ham_6":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(40);
                                break;
                            case "buttonPlace_horizontal":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(41);
                                break;
                            case "buttonPlace_vertical":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(42);
                                break;
                            case "buttonPlace_custom":
                                buttonPlaceEnum = ButtonPlaceEnum.getEnum(43);
                                break;
                        }

                    }
                    break;
                case BMB_BUTTON_PLACE_ALIGNMENT_ENUM:
                    if(attrValue != null && !"".equals(attrValue)){
                        switch(attrValue){
                            case "buttonPlaceAlignment_center":
                                buttonPlaceAlignmentEnum = ButtonPlaceAlignmentEnum.getEnum(0);
                                break;
                            case "buttonPlaceAlignment_top":
                                buttonPlaceAlignmentEnum = ButtonPlaceAlignmentEnum.getEnum(1);
                                break;
                            case "buttonPlaceAlignment_bottom":
                                buttonPlaceAlignmentEnum = ButtonPlaceAlignmentEnum.getEnum(2);
                                break;
                            case "buttonPlaceAlignment_left":
                                buttonPlaceAlignmentEnum = ButtonPlaceAlignmentEnum.getEnum(3);
                                break;
                            case "buttonPlaceAlignment_right":
                                buttonPlaceAlignmentEnum = ButtonPlaceAlignmentEnum.getEnum(4);
                                break;
                            case "buttonPlaceAlignment_tl":
                                buttonPlaceAlignmentEnum = ButtonPlaceAlignmentEnum.getEnum(5);
                                break;
                            case "buttonPlaceAlignment_tr":
                                buttonPlaceAlignmentEnum = ButtonPlaceAlignmentEnum.getEnum(6);
                                break;
                            case "buttonPlaceAlignment_bl":
                                buttonPlaceAlignmentEnum = ButtonPlaceAlignmentEnum.getEnum(7);
                                break;
                            case "buttonPlaceAlignment_br":
                                buttonPlaceAlignmentEnum = ButtonPlaceAlignmentEnum.getEnum(8);
                                break;

                        }

                    }
                    break;
                case BMB_BUTTON_HORIZONTAL_MARGIN:
                    if(attrValue != null && !"".equals(attrValue)){
                        buttonHorizontalMargin = attr.getFloatValue();
                    }
                    break;
                case BMB_BUTTON_VERTICAL_MARGIN:
                    if(attrValue != null && !"".equals(attrValue)){
                        buttonVerticalMargin = attr.getFloatValue();
                    }
                    break;
                case BMB_BUTTON_INCLINED_MARGIN:
                    if(attrValue != null && !"".equals(attrValue)){
                        buttonInclinedMargin = attr.getFloatValue();
                    }
                    break;
                case BMB_BUTTON_TOP_MARGIN:
                    if(attrValue != null && !"".equals(attrValue)){
                        buttonTopMargin = attr.getFloatValue();
                    }
                    break;
                case BMB_BUTTON_BOTTOM_MARGIN:
                    if(attrValue != null && !"".equals(attrValue)){
                        buttonBottomMargin = attr.getFloatValue();
                    }
                    break;
                case BMB_BUTTON_LEFT_MARGIN:
                    if(attrValue != null && !"".equals(attrValue)){
                        buttonLeftMargin = attr.getFloatValue();
                    }
                    break;
                case BMB_BUTTON_RIGHT_MARGIN:
                    if(attrValue != null && !"".equals(attrValue)){
                        buttonRightMargin = attr.getFloatValue();
                    }
                    break;
                case BMB_BOTTOM_HAM_BUTTON_TOP_MARGIN:
                    if(attrValue != null && !"".equals(attrValue)){
                        bottomHamButtonTopMargin = attr.getFloatValue();
                    }
                    break;
            }
        }
    }
    private void initAttrs(Context contextIn, AttrSet attrs)  {
        ResourceManager resManager = contextIn.getResourceManager();
        try {
            // Basic
            cacheOptimization = Util.getBoolean(resManager, ResourceTable.Boolean_default_bmb_cacheOptimization, ResourceTable.Boolean_default_bmb_cacheOptimization);
            boomInWholeScreen = Util.getBoolean(resManager, ResourceTable.Boolean_default_bmb_boomInWholeScreen, ResourceTable.Boolean_default_bmb_boomInWholeScreen);
            inList = Util.getBoolean(resManager, ResourceTable.Boolean_default_bmb_inList, ResourceTable.Boolean_default_bmb_inList);
            inFragment = Util.getBoolean(resManager, ResourceTable.Boolean_default_bmb_inFragment,ResourceTable.Boolean_default_bmb_inFragment);
            isBackPressListened = Util.getBoolean(resManager, ResourceTable.Boolean_default_bmb_backPressListened, ResourceTable.Boolean_default_bmb_backPressListened);
            isOrientationAdaptable = Util.getBoolean(resManager, ResourceTable.Boolean_default_bmb_orientationAdaptable, ResourceTable.Boolean_default_bmb_orientationAdaptable);

            // Shadow
            shadowEffect = Util.getBoolean(resManager, ResourceTable.Boolean_default_bmb_shadow_effect, ResourceTable.Boolean_default_bmb_shadow_effect);
            shadowRadius = Util.getDimenSize(resManager, ResourceTable.Float_default_bmb_shadow_radius, ResourceTable.Float_default_bmb_shadow_radius);
            shadowOffsetX = Util.getDimenOffset(resManager,ResourceTable.Float_default_bmb_shadow_offset_x, ResourceTable.Float_default_bmb_shadow_offset_x);
            shadowOffsetY = Util.getDimenOffset(resManager, ResourceTable.Float_default_bmb_shadow_offset_y, ResourceTable.Float_default_bmb_shadow_offset_y);
            shadowColor = Util.getColor(resManager, ResourceTable.Color_default_bmb_shadow_color, ResourceTable.Color_default_bmb_shadow_color);

            // Button
            buttonRadius = Util.getDimenSize(resManager, ResourceTable.Float_default_bmb_button_radius, ResourceTable.Float_default_bmb_button_radius);
            buttonEnum = ButtonEnum.getEnum(Util.getInt(resManager, ResourceTable.Integer_default_bmb_button_enum, ResourceTable.Integer_default_bmb_button_enum));
            backgroundEffect = Util.getBoolean(resManager, ResourceTable.Boolean_default_bmb_background_effect, ResourceTable.Boolean_default_bmb_background_effect);
            rippleEffect = Util.getBoolean(resManager, ResourceTable.Boolean_default_bmb_ripple_effect, ResourceTable.Boolean_default_bmb_ripple_effect);
            normalColor = Util.getColor(resManager, ResourceTable.Color_default_bmb_normal_color, ResourceTable.Color_default_bmb_normal_color);
            highlightedColor = Util.getColor(resManager, ResourceTable.Color_default_bmb_highlighted_color, ResourceTable.Color_default_bmb_highlighted_color);
            if (highlightedColor == Color.TRANSPARENT) highlightedColor = Util.getDarkerColor(normalColor);
            unableColor = Util.getColor(resManager, ResourceTable.Color_default_bmb_unable_color, ResourceTable.Color_default_bmb_unable_color);
            if (unableColor == Color.TRANSPARENT) unableColor = Util.getLighterColor(normalColor);
            draggable = Util.getBoolean(resManager, ResourceTable.Boolean_default_bmb_draggable, ResourceTable.Boolean_default_bmb_draggable);
            edgeInsetsInParentView = new Rect(0, 0, 0, 0);
            edgeInsetsInParentView.left = Util.getDimenOffset(resManager, ResourceTable.Float_default_bmb_edgeInsetsLeft, ResourceTable.Float_default_bmb_edgeInsetsLeft);
            edgeInsetsInParentView.top = Util.getDimenOffset(resManager, ResourceTable.Float_default_bmb_edgeInsetsTop, ResourceTable.Float_default_bmb_edgeInsetsTop);
            edgeInsetsInParentView.right = Util.getDimenOffset(resManager, ResourceTable.Float_default_bmb_edgeInsetsRight, ResourceTable.Float_default_bmb_edgeInsetsRight);
            edgeInsetsInParentView.bottom = Util.getDimenOffset(resManager, ResourceTable.Float_default_bmb_edgeInsetsBottom, ResourceTable.Float_default_bmb_edgeInsetsBottom);

            // Piece
            dotRadius = Util.getDimenSize(resManager, ResourceTable.Float_default_bmb_dotRadius, ResourceTable.Float_default_bmb_dotRadius);
            hamWidth = Util.getDimenSize(resManager, ResourceTable.Float_default_bmb_hamWidth, ResourceTable.Float_default_bmb_hamWidth);
            hamHeight = Util.getDimenSize(resManager, ResourceTable.Float_default_bmb_hamHeight, ResourceTable.Float_default_bmb_hamHeight);
            pieceCornerRadius = Util.getDimenSize(resManager, ResourceTable.Float_default_bmb_pieceCornerRadius, ResourceTable.Float_default_bmb_pieceCornerRadius);
            pieceHorizontalMargin = Util.getDimenOffset(resManager, ResourceTable.Float_default_bmb_pieceHorizontalMargin, ResourceTable.Float_default_bmb_pieceHorizontalMargin);
            pieceVerticalMargin = Util.getDimenOffset(resManager, ResourceTable.Float_default_bmb_pieceVerticalMargin, ResourceTable.Float_default_bmb_pieceVerticalMargin);
            pieceInclinedMargin = Util.getDimenOffset(resManager, ResourceTable.Float_default_bmb_pieceInclinedMargin, ResourceTable.Float_default_bmb_pieceInclinedMargin);
            shareLineLength = Util.getDimenSize(resManager, ResourceTable.Float_default_bmb_sharedLineLength, ResourceTable.Float_default_bmb_sharedLineLength);
            shareLine1Color = Util.getColor(resManager, ResourceTable.Color_default_bmb_shareLine1Color, ResourceTable.Color_default_bmb_shareLine1Color);
            shareLine2Color = Util.getColor(resManager, ResourceTable.Color_default_bmb_shareLine2Color, ResourceTable.Color_default_bmb_shareLine2Color);
            shareLineWidth = Util.getDimenSize(resManager, ResourceTable.Float_default_bmb_shareLineWidth, ResourceTable.Float_default_bmb_shareLineWidth);
            piecePlaceEnum = PiecePlaceEnum.getEnum(Util.getInt(resManager, ResourceTable.Integer_default_bmb_pieceEnum, ResourceTable.Integer_default_bmb_pieceEnum));

            // Animation
            dimColor = Util.getColor(resManager, ResourceTable.Color_default_bmb_dimColor, ResourceTable.Color_default_bmb_dimColor);
            showDuration = Util.getInt(resManager, ResourceTable.Integer_default_bmb_showDuration, ResourceTable.Integer_default_bmb_showDuration);
            showDelay = Util.getInt(resManager, ResourceTable.Integer_default_bmb_showDelay, ResourceTable.Integer_default_bmb_showDelay);
            hideDuration = Util.getInt(resManager, ResourceTable.Integer_default_bmb_hideDuration, ResourceTable.Integer_default_bmb_hideDuration);
            hideDelay = Util.getInt(resManager, ResourceTable.Integer_default_bmb_hideDelay, ResourceTable.Integer_default_bmb_hideDelay);
            cancelable = Util.getBoolean(resManager, ResourceTable.Boolean_default_bmb_cancelable, ResourceTable.Boolean_default_bmb_cancelable);
            autoHide = Util.getBoolean(resManager, ResourceTable.Boolean_default_bmb_autoHide, ResourceTable.Boolean_default_bmb_autoHide);
            orderEnum = OrderEnum.getEnum(Util.getInt(resManager, ResourceTable.Integer_default_bmb_orderEnum, ResourceTable.Integer_default_bmb_orderEnum));
            frames = Util.getInt(resManager, ResourceTable.Integer_default_bmb_frames, ResourceTable.Integer_default_bmb_frames);
            boomEnum = BoomEnum.getEnum(Util.getInt(resManager, ResourceTable.Integer_default_bmb_boomEnum, ResourceTable.Integer_default_bmb_boomEnum));
            showMoveEaseEnum = EaseEnum.getEnum(Util.getInt(resManager, ResourceTable.Integer_default_bmb_showMoveEaseEnum, ResourceTable.Integer_default_bmb_showMoveEaseEnum));
            showScaleEaseEnum = EaseEnum.getEnum(Util.getInt(resManager, ResourceTable.Integer_default_bmb_showScaleEaseEnum, ResourceTable.Integer_default_bmb_showScaleEaseEnum));
            showRotateEaseEnum = EaseEnum.getEnum(Util.getInt(resManager, ResourceTable.Integer_default_bmb_showRotateEaseEnum, ResourceTable.Integer_default_bmb_showRotateEaseEnum));
            hideMoveEaseEnum = EaseEnum.getEnum(Util.getInt(resManager, ResourceTable.Integer_default_bmb_hideMoveEaseEnum, ResourceTable.Integer_default_bmb_hideMoveEaseEnum));
            hideScaleEaseEnum = EaseEnum.getEnum(Util.getInt(resManager, ResourceTable.Integer_default_bmb_hideScaleEaseEnum, ResourceTable.Integer_default_bmb_hideScaleEaseEnum));
            hideRotateEaseEnum = EaseEnum.getEnum(Util.getInt(resManager, ResourceTable.Integer_default_bmb_hideRotateEaseEnum, ResourceTable.Integer_default_bmb_hideRotateEaseEnum));
            rotateDegree = Util.getInt(resManager, ResourceTable.Integer_default_bmb_rotateDegree, ResourceTable.Integer_default_bmb_rotateDegree);
            use3DTransformAnimation = Util.getBoolean(resManager, ResourceTable.Boolean_default_bmb_use3DTransformAnimation, ResourceTable.Boolean_default_bmb_use3DTransformAnimation);
            autoBoom = Util.getBoolean(resManager, ResourceTable.Boolean_default_bmb_autoBoom, ResourceTable.Boolean_default_bmb_autoBoom);
            autoBoomImmediately = Util.getBoolean(resManager, ResourceTable.Boolean_default_bmb_autoBoomImmediately, ResourceTable.Boolean_default_bmb_autoBoomImmediately);

            // Boom buttons
            buttonPlaceEnum = ButtonPlaceEnum.getEnum(Util.getInt(resManager, ResourceTable.Integer_default_bmb_buttonPlaceEnum, ResourceTable.Integer_default_bmb_buttonPlaceEnum));
            buttonPlaceAlignmentEnum = ButtonPlaceAlignmentEnum.getEnum(Util.getInt(resManager, ResourceTable.Integer_default_bmb_buttonPlaceAlignmentEnum, ResourceTable.Integer_default_bmb_buttonPlaceAlignmentEnum));
            buttonHorizontalMargin = Util.getDimenOffset(resManager, ResourceTable.Float_default_bmb_buttonHorizontalMargin, ResourceTable.Float_default_bmb_buttonHorizontalMargin);
            buttonVerticalMargin = Util.getDimenOffset(resManager, ResourceTable.Float_default_bmb_buttonVerticalMargin, ResourceTable.Float_default_bmb_buttonVerticalMargin);
            buttonInclinedMargin = Util.getDimenOffset(resManager, ResourceTable.Float_default_bmb_buttonInclinedMargin, ResourceTable.Float_default_bmb_buttonInclinedMargin);
            buttonTopMargin = Util.getDimenOffset(resManager, ResourceTable.Float_default_bmb_buttonTopMargin, ResourceTable.Float_default_bmb_buttonTopMargin);
            buttonBottomMargin = Util.getDimenOffset(resManager, ResourceTable.Float_default_bmb_buttonBottomMargin, ResourceTable.Float_default_bmb_buttonBottomMargin);
            buttonLeftMargin = Util.getDimenOffset(resManager, ResourceTable.Float_default_bmb_buttonLeftMargin, ResourceTable.Float_default_bmb_buttonLeftMargin);
            buttonRightMargin = Util.getDimenOffset(resManager, ResourceTable.Float_default_bmb_buttonRightMargin, ResourceTable.Float_default_bmb_buttonRightMargin);
            bottomHamButtonTopMargin = Util.getDimenOffset(resManager, ResourceTable.Float_default_bmb_bottomHamButtonTopMargin, ResourceTable.Float_default_bmb_bottomHamButtonTopMargin);

            attrsLayout(context, attrs);
        } catch (NotExistException e) {
            e.printStackTrace();
        } catch (WrongTypeException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {

        }
    }

    private void initShadow() {
        if (shadow == null) shadow = (BMBShadow) findComponentById(ResourceTable.Id_shadow);
        boolean hasShadow = shadowEffect && backgroundEffect && !inList;
        shadow.setShadowEffect(hasShadow);
        if (hasShadow) {
            shadow.setShadowOffsetX(shadowOffsetX);
            shadow.setShadowOffsetY(shadowOffsetY);
            shadow.setShadowColor(shadowColor);
            shadow.setShadowRadius(shadowRadius);
            shadow.setShadowCornerRadius(shadowRadius + buttonRadius);
        } else {
            shadow.clearShadow();
        }
    }

    private void initButton() {
        if (button == null) button = (StackLayout) findComponentById(ResourceTable.Id_button);
        button.setClickedListener(new ClickedListener() {
            @Override
            public void onClick(Component v) {
                boom();
            }
        });
        button.setKeyEventListener(new Component.KeyEventListener()
        {
            public boolean onKeyEvent(Component var1, KeyEvent var2)
            {
                if (KeyEvent.KEY_BACK == var2.getKeyCode()
                        && isBackPressListened
                        && (boomStateEnum == BoomStateEnum.WillBoom || boomStateEnum == BoomStateEnum.DidBoom)) {
                    reboom();
                    return true;
                }
                return false;
            }
        });
        initDraggableTouchListener();

        setButtonSize();
        setButtonBackground();
    }

    private void setButtonSize() {
        LayoutConfig params = (LayoutConfig) button.getLayoutConfig();
        params.width = buttonRadius * 2;
        params.height = buttonRadius * 2;
        button.setLayoutConfig(params);
    }

    private void setButtonBackground() {
        if (backgroundEffect && !inList) {
            StateElement stateListDrawable = Util.getOvalStateListBitmapDrawable(
                    button,
                    buttonRadius,
                    normalColor,
                    highlightedColor,
                    unableColor);
            Util.setDrawable(button, stateListDrawable);
        } else {
            Util.setDrawable(button, Util.getSystemDrawable(context, ResourceTable.Layout_bmb_ham_button));
        }
    }

    //endregion

    private void ____________________________Place_Pieces() {}

    //region Place Pieces
    private void clearPieces() {
        if (pieces != null) for (BoomPiece piece : pieces) button.removeComponent(piece);
        if (pieces != null) pieces.clear();
    }

    private void createPieces() {
        calculatePiecePositions();
        int pieceNumber = pieceNumber();
        pieces = new ArrayList<>(pieceNumber);
        for (int i = 0; i < pieceNumber; i++)
            pieces.add(PiecePlaceManager.createPiece(this, boomButtonBuilders.get(i)));
    }

    private void placePieces() {
        ArrayList<Integer> indexes;
        if (piecePlaceEnum == Share)
            indexes = AnimationManager.getOrderIndex(OrderEnum.DEFAULT, pieces.size());
        else
            indexes = AnimationManager.getOrderIndex(orderEnum, pieces.size());
        // Reverse to keep the former pieces are above than the latter(z-axis)
        // So the early-animating pieces are above than the later ones
        for (int i = indexes.size() - 1; i >= 0; i--) button.addComponent(pieces.get(indexes.get(i)));
    }

    private void placePiecesAtPositions() {
        int pieceNumber = pieceNumber();
        for (int i = 0; i < pieceNumber; i++) {
            pieces.get(i).place(piecePositions.get(i));
        }
    }

    private void calculatePiecePositions() {
        switch (buttonEnum) {
            case SimpleCircle:
            case TextInsideCircle:
            case TextOutsideCircle:
                if (piecePlaceEnum == Share)
                    piecePositions = PiecePlaceManager.getShareDotPositions(this,
                            new Point((float)button.getWidth(), (float)button.getHeight()),
                            boomButtonBuilders.size());
                else
                    piecePositions = PiecePlaceManager.getDotPositions(this,
                            new Point(button.getWidth(), button.getHeight()));
                break;
            case Ham:
                piecePositions = PiecePlaceManager.getHamPositions(this,
                        new Point(button.getWidth(), button.getHeight()));
                break;
            case Unknown:
                throw new RuntimeException("The button-enum is unknown!");
        }
    }

    //endregion

    private void ____________________________Touch() {}
    //region Touch

    private void initDraggableTouchListener() {
        if (button == null) return;
        if (!draggable) button.setTouchEventListener(null);
        else {
            button.setTouchEventListener(new TouchEventListener() {
                @Override
                public boolean onTouchEvent(Component v, TouchEvent event) {
                    switch (event.getAction()) {
                        case TouchEvent.PRIMARY_POINT_DOWN:
                            if (draggable) {
                                startPositionX = event.getPointerPosition(0).getX();
                                startPositionY = event.getPointerPosition(0).getY();
                                lastMotionX = event.getPointerPosition(0).getX();
                                lastMotionY = event.getPointerPosition(0).getY();
                            }
                            break;
                        case TouchEvent.POINT_MOVE:
                            if (Math.abs(lastMotionX - event.getPointerPosition(0).getX()) > 10
                                    || Math.abs(lastMotionY - event.getPointerPosition(0).getY()) > 10)
                                ableToStartDragging = true;
                            if (draggable && ableToStartDragging) {
                                isDragging = true;
                                if (shadow != null) {
                                    setContentPositionX(event.getPointerScreenPosition(0).getX() - startPositionX);
                                    setContentPositionY(event.getPointerScreenPosition(0).getY() - startPositionY - shadow.getHeight() - shadow.getPaddingTop() - shadow.getPaddingBottom());
                                }
                            } else {
                                ableToStartDragging = false;
                            }
                            break;
                        case TouchEvent.PRIMARY_POINT_UP:
                            if (isDragging) {
                                ableToStartDragging = false;
                                isDragging = false;
                                needToCalculateStartPositions = true;
                                preventDragOutside();
                                button.setPressState(false);
                                return true;
                            }
                            break;
                        case TouchEvent.CANCEL:
                            if (isDragging) {
                                ableToStartDragging = false;
                                isDragging = false;
                                needToCalculateStartPositions = true;
                                preventDragOutside();
                                return true;
                            }
                            break;
                    }
                    return true;
                }
            });
        }
    }

    //endregion

    private void ____________________________Animation() {}
    //region Animation

    /**
     * Whether BMB is animating.
     *
     * @return Is animating.
     */
    public boolean isAnimating() {
        return animatingViewNumber != 0; }

    /**
     * Whether the BMB has finished booming.
     *
     * @return whether the BMB has finished booming
     */
    public boolean isBoomed() {

        return boomStateEnum == BoomStateEnum.DidBoom;
    }

    /**
     * Whether the BMB has finished ReBooming.
     *
     * @return whether the BMB has finished ReBooming
     */
    public boolean isReBoomed() {
        return boomStateEnum == BoomStateEnum.DidReboom; }

    /**
     * Boom the BMB!
     */
    public void boom() {
        innerBoom(false); }

    /**
     * Boom the BMB with duration 0!
     */
    public void boomImmediately() {

        innerBoom(true);
    }

    private void innerBoom(boolean immediately) {
        if (isAnimating() || boomStateEnum != BoomStateEnum.DidReboom) return;
        ExceptionManager.judge(this, boomButtonBuilders);
        boomStateEnum = BoomStateEnum.WillBoom;
        if (onBoomListener != null) onBoomListener.onBoomWillShow();
        calculateStartPositions(false);
        createButtons();
        dimBackground(immediately);
        startBoomAnimations(immediately);
        startBoomAnimationForFadeViews(immediately);
        if (isBackPressListened) {
            setFocusable(1);
            setTouchFocusable(true);
            requestFocus();
        }
    }

    /**
     * Re-boom the BMB!
     */
    public void reboom() {
        innerReboom(false); }

    /**
     * Re-boom the BMB with duration 0!
     */
    public void reboomImmediately() {

        innerReboom(true);
    }

    private void innerReboom(boolean immediately) {
        if (isAnimating() || boomStateEnum != BoomStateEnum.DidBoom) return;
        boomStateEnum = BoomStateEnum.WillReboom;
        if (onBoomListener != null) onBoomListener.onBoomWillHide();
        lightBackground(immediately);
        startReboomAnimations(immediately);
        startReboomAnimationForFadeViews(immediately);
        if (isBackPressListened) {
            setFocusable(0);
            setTouchFocusable(false);
        }
    }

    private void dimBackground(boolean immediately) {
        createBackground();
        Util.setVisibility(VISIBLE, background);
        long duration = immediately ? 1 : showDuration + showDelay * (pieces.size() - 1);
        background.dim(duration, new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
            }

            @Override
            public void onStop(Animator animator) {
            }

            @Override
            public void onCancel(Animator animator) {
            }

            @Override
            public void onPause(Animator animator) {
            }

            @Override
            public void onResume(Animator animator) {
            }

            @Override
            public void onEnd(Animator animation) {
                boomStateEnum = BoomStateEnum.DidBoom;
                if (onBoomListener != null) onBoomListener.onBoomDidShow();
            }
        });
        if (piecePlaceEnum == Share) {
            AnimationManager.animate(shareLinesView, "showProcess", 0, duration,
                    Ease.getInstance(EaseEnum.Linear), 0f, 1f);
        }
    }

    private void lightBackground(boolean immediately) {
        createBackground();
        long duration = immediately ? 1 : hideDuration + hideDelay * (pieces.size() - 1);
        background.light(duration, null);
        if (piecePlaceEnum == Share) {
            AnimationManager.animate( shareLinesView, "hideProcess", 0, duration,
                    Ease.getInstance(EaseEnum.Linear), 0f, 1f);
        }
    }

    private void finishReboomAnimations() {
        if (isAnimating()) {
            return;
        }
        boomStateEnum = BoomStateEnum.DidReboom;
        if (onBoomListener != null) onBoomListener.onBoomDidHide();
        background.setVisibility(HIDE);
        clearViews(false);
    }

    private void startBoomAnimations(boolean immediately) {
        if (background != null) background.removeAllComponents();
        calculateEndPositions();
        ArrayList<Integer> indexes;
        if (piecePlaceEnum == Share)
            indexes = AnimationManager.getOrderIndex(OrderEnum.DEFAULT, pieces.size());
        else indexes = AnimationManager.getOrderIndex(orderEnum, pieces.size());
        // Todo Fix the following bug
        // There is a strange bug when use3DTransformAnimation is true.
        // The last boom-button that rebooms has a strange behavior with a duplicate shadow.
        // So we need to recreate it.
        if (lastReboomIndex != -1 && use3DTransformAnimation)
            boomButtons.set(lastReboomIndex, boomButtonBuilders.get(lastReboomIndex)
                    .innerListener(this).index(lastReboomIndex).build(context));
        // Reverse to keep the former boom-buttons are above than the latter(z-axis)
        // So the early-animating boom-buttons are above than the later ones
        for (int i = indexes.size() - 1; i >= 0; i--) {
            int index = indexes.get(i);
            BoomButton boomButton = boomButtons.get(index);
            Point startPosition = new Point(
                    startPositions.get(index).getPointX() - boomButton.centerPoint.getPointX(),
                    startPositions.get(index).getPointY() - boomButton.centerPoint.getPointY());
            putBoomButtonInBackground(boomButton, startPosition);
            startEachBoomAnimation(pieces.get(index), boomButton, startPosition,
                    endPositions.get(index), i, immediately);
        }
    }

    private void startReboomAnimations(boolean immediately) {
        ArrayList<Integer> indexes;
        if (piecePlaceEnum == Share)
            indexes = AnimationManager.getOrderIndex(OrderEnum.REVERSE, pieces.size());
        else indexes = AnimationManager.getOrderIndex(orderEnum, pieces.size());
        lastReboomIndex = indexes.get(indexes.size() - 1);
        for (Integer index : indexes) this.moveChildToFront(boomButtons.get(index));
        for (int i = 0; i < indexes.size(); i++) {
            int index = indexes.get(i);
            BoomButton boomButton = boomButtons.get(index);
            boomButton.place(
                    endPositions.get(index).getPointXToInt(),
                    endPositions.get(index).getPointYToInt(),
                    boomButton.trueWidth(),
                    boomButton.trueHeight());
            Point startPosition = new Point(
                    startPositions.get(index).getPointX() - boomButton.centerPoint.getPointX(),
                    startPositions.get(index).getPointY() - boomButton.centerPoint.getPointY());
            startEachReboomAnimation( pieces.get(index), boomButton,
                    endPositions.get(index), startPosition, i, immediately);
        }
    }

    private void startEachBoomAnimation(final BoomPiece piece,
                                        final BoomButton boomButton,
                                        final Point startPosition,
                                        final Point endPosition,
                                        final int delayFactor,
                                        final boolean immediately) {
        if (isBatterySaveModeTurnOn()) {
            Util.commonEventHandler().postTask(new Runnable() {
                @Override
                public void run() {
                    innerStartEachBoomAnimation(piece, boomButton, startPosition, endPosition, delayFactor, immediately);
                }
            });
        } else {
            innerStartEachBoomAnimation(piece, boomButton, startPosition, endPosition, delayFactor, immediately);
        }
    }

    private int getCurveType(BoomEnum boomEnumIn)
    {
        int curveType = Animator.CurveType.LINEAR;
        switch (boomEnumIn) {
            case LINE:
                curveType = Animator.CurveType.LINEAR;
                break;
            case PARABOLA_1:
                curveType = Animator.CurveType.CUBIC_HERMITE;
                break;
            case PARABOLA_2:
                curveType = Animator.CurveType.CUBIC_BEZIER_STANDARD;
                break;
            case PARABOLA_3:
                curveType = Animator.CurveType.CUBIC_BEZIER_SMOOTH;
                break;
            case PARABOLA_4:
                curveType = Animator.CurveType.CUBIC_BEZIER_SHARP;
                break;
            case HORIZONTAL_THROW_1:
                curveType = Animator.CurveType.CUBIC_BEZIER_RHYTHM;
                break;
            case HORIZONTAL_THROW_2:
                curveType = Animator.CurveType.CUBIC_BEZIER_FRICTION;
                break;
            case RANDOM:
                curveType = getCurveType(BoomEnum.values()[new Random().nextInt(BoomEnum.RANDOM.getValue())]);
                break;
            case Unknown:
                throw new RuntimeException("Unknown boom-enum!");
        }
        return curveType;
    }

    private void innerStartEachBoomAnimation(final BoomPiece piece,
                                             final BoomButton boomButton,
                                             Point startPosition,
                                             Point endPosition,
                                             int delayFactor,
                                             boolean immediately) {
        animatingViewNumber++;
        float[] xs = new float[frames + 1];
        float[] ys = new float[frames + 1];
        float scaleX = piece.getWidth() * 1.0f / boomButton.contentWidth();
        float scaleY = piece.getHeight() * 1.0f / boomButton.contentHeight();
        long delay = immediately ? 1 : showDelay * delayFactor;
        long duration = immediately ? 1 : showDuration;
        boomButton.setSelfScaleAnchorPoints();
        boomButton.setScaleX(scaleX);
        boomButton.setScaleY(scaleY);
        boomButton.hideAllGoneViews();
        AnimationManager.calculateShowXY(boomEnum,
            new Point(background.getLayoutConfig().width, background.getLayoutConfig().height),
            Ease.getInstance(showMoveEaseEnum), frames, startPosition, endPosition, xs, ys);

        AnimatorProperty animator = boomButton.createAnimatorProperty();
        animator.setDelay(delay);
        animator.setDuration(duration);
        animator.setCurveType(getCurveType(boomEnum));
        animator.setTarget(boomButton);
        animator.setLoopedCount(0);
        animator.moveFromX(startPosition.getPointX())
                .moveToX(endPosition.getPointX())
                .moveFromY(startPosition.getPointY())
                .moveToY(endPosition.getPointY())
                .alphaFrom(0)
                .alpha(1)
                .scaleXFrom(scaleX)
                .scaleX(1)
                .scaleYFrom(scaleY)
                .scaleY(1);

        animator.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
                Util.setVisibility(INVISIBLE, piece);
                Util.setVisibility(VISIBLE, boomButton);
                boomButton.willShow();
            }

            @Override
            public void onStop(Animator animator) {

            }

            @Override
            public void onCancel(Animator animator) {

            }

            @Override
            public void onEnd(Animator animator) {
                boomButton.didShow();
                animatingViewNumber--;
            }

            @Override
            public void onPause(Animator animator) {

            }

            @Override
            public void onResume(Animator animator) {

            }
        });
        animator.start();
    }

    private void startEachReboomAnimation(final BoomPiece piece,
                                          final BoomButton boomButton,
                                          Point startPosition,
                                          Point endPosition,
                                          final int delayFactor,
                                          boolean immediately) {
        animatingViewNumber++;
        float[] xs = new float[frames + 1];
        float[] ys = new float[frames + 1];
        float scaleX = piece.getWidth() * 1.0f / boomButton.contentWidth();
        float scaleY = piece.getHeight() * 1.0f / boomButton.contentHeight();
        long delay = immediately ? 1 : hideDelay * delayFactor;
        long duration = immediately ? 1 : hideDuration;

        AnimationManager.calculateHideXY(
                boomEnum,
                new Point(background.getLayoutConfig().width, background.getLayoutConfig().height),
                Ease.getInstance(hideMoveEaseEnum), frames, startPosition, endPosition, xs, ys);

        AnimationManager.animate("alpha", delay, duration, Ease.getInstance(EaseEnum.Linear), boomButton.goneViews(), 1, 0);
        AnimatorProperty animator = boomButton.createAnimatorProperty();
        animator.setDelay(delay);
        animator.setDuration(duration);
        animator.setCurveType(getCurveType(boomEnum));
        animator.setTarget(boomButton);
        animator.setLoopedCount(0);
        animator.alphaFrom(1)
                .alpha(0)
                .scaleXFrom(1)
                .scaleX(scaleX)
                .scaleYFrom(1)
                .scaleY(scaleY)
                .moveFromX(startPosition.getPointX())
                .moveToX(endPosition.getPointX())
                .moveFromY(startPosition.getPointY())
                .moveToY(endPosition.getPointY());

        animator.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
                boomButton.willHide();
            }

            @Override
            public void onStop(Animator animator) {

            }

            @Override
            public void onCancel(Animator animator) {

            }

            @Override
            public void onEnd(Animator animator) {
                Util.setVisibility(VISIBLE, piece);
                Util.setVisibility(INVISIBLE, boomButton);
                boomButton.didHide();
                animatingViewNumber--;
                finishReboomAnimations();
            }

            @Override
            public void onPause(Animator animator) {

            }

            @Override
            public void onResume(Animator animator) {

            }
        });
        animator.start();
    }

    //endregion

    private void ____________________________Support_Methods() {}
    //region Support Methods

    /**
     * post Layout
     */
    @Override
    public void  postLayout() {
        super.postLayout();
    }

    /**
     * build Button
     */
    public void buildButton()
    {
        needToLayout = true;
        onLayout();
    }

    /**
     * onLayout
     */
    protected void onLayout() {
        if (isOrientationChanged) {
            reLayoutAfterOrientationChanged();
            isOrientationChanged = false;
        }
        if (needToLayout) {
            if (inList) delayToDoLayoutJobs();
            else doLayoutJobs();
        }
        needToLayout = false;
    }

    /**
     * do LayoutJobs
     */
    private void doLayoutJobs() {
        if (uninitializedBoomButtons()) return;
        clearPieces();
        createPieces();
        placeShareLinesView();
        placePieces();
        placePiecesAtPositions();
        calculateStartPositions(false);
        setShareLinesViewData();
    }

    /**
     * create Background
     */
    private void createBackground() {
        if (background == null) {
            background = new BackgroundView(context, this);
        }
    }

    /**
     * getParentView
     * @return
     */
    protected ComponentContainer getParentView() {
        if (boomInWholeScreen) {
//            Ability activity = Util.scanForActivity(context);
//            if (activity == null) {
//                return (ComponentContainer) getComponentParent();
//            } else {
//                return (ComponentContainer) getComponentParent();
//            }
            Component component = this;
            if (rootContainer != null) {
                component = rootContainer;
            }
            ComponentContainer container = Util.scanTopComponentContainer(component);
            if (container == null) {
                return (ComponentContainer) getComponentParent();
            } else {
                return container;
            }
        } else {
            return (ComponentContainer) getComponentParent();
        }
    }

    /**
     *
     * clear Background
     */
    private void clearBackground() {
        Util.setVisibility(HIDE, background);
        if (!cacheOptimization || inList || inFragment) {
            background.removeAllComponents();
            ((ComponentContainer)background.getComponentParent()).removeComponent(background);
            background = null;
        }
    }

    /**
     * create Buttons
     */
    private void createButtons() {
        if (!needToCreateButtons) return;
        needToCreateButtons = false;
        boomButtons = new ArrayList<>(pieces.size());
        int buttonNumber = pieces.size();
        for (int i = 0; i < boomButtonBuilders.size(); i++)
            boomButtons.add(boomButtonBuilders.get(i).innerListener(this).index(i).build(context));
        switch (buttonEnum) {
            case SimpleCircle:
                simpleCircleButtonRadius = ((SimpleCircleButton.Builder)
                        boomButtonBuilders.get(0)).getButtonRadius();
                break;
            case TextInsideCircle:
                textInsideCircleButtonRadius = ((TextInsideCircleButton.Builder)
                        boomButtonBuilders.get(0)).getButtonRadius();
                break;
            case TextOutsideCircle:
                textOutsideCircleButtonWidth = ((TextOutsideCircleButton.Builder)
                        boomButtonBuilders.get(0)).getButtonContentWidth();
                textOutsideCircleButtonHeight = ((TextOutsideCircleButton.Builder)
                        boomButtonBuilders.get(0)).getButtonContentHeight();
                break;
            case Ham:
                hamButtonWidth = ((HamButton.Builder)
                        boomButtonBuilders.get(0)).getButtonWidth();
                hamButtonHeight = ((HamButton.Builder)
                        boomButtonBuilders.get(0)).getButtonHeight();
                break;
        }
    }

    /**
     *
     * on Background Clicked
     */
    protected void onBackgroundClicked() {
        if (isAnimating()) return;
        if (onBoomListener != null) onBoomListener.onBackgroundClick();
        if (cancelable) reboom();
    }

    /**
     * calculate Start Positions
     * @param force
     */
    private void calculateStartPositions(boolean force) {
        if (!(force || needToCalculateStartPositions || inList || inFragment)) return;
        if (!force) needToCalculateStartPositions = false;
        startPositions = new ArrayList<>(pieceNumber());
        ComponentContainer rootView = getParentView();
        int[] rootViewLocation = rootView.getLocationOnScreen();
        for (int i = 0; i < pieces.size(); i++) {
            Point pieceCenterInRootView = new Point();
            int[] buttonLocation = button.getLocationOnScreen();
            float x = buttonLocation[0] + piecePositions.get(i).left
                    - rootViewLocation[0] + pieces.get(i).getLayoutConfig().width / 2;
            float y = buttonLocation[1] + piecePositions.get(i).top
                    - rootViewLocation[1] + pieces.get(i).getLayoutConfig().height / 2;
            pieceCenterInRootView.modify(x, y);
            startPositions.add(pieceCenterInRootView);
        }
    }

    /**
     * calculate End Positions
     */
    private void calculateEndPositions() {
        Point parentSize = new Point(background.getLayoutConfig().width,
                background.getLayoutConfig().height);
        switch (buttonEnum) {
            case SimpleCircle:
                endPositions = ButtonPlaceManager.getPositions(parentSize,
                        simpleCircleButtonRadius, boomButtonBuilders.size(), this);
                break;
            case TextInsideCircle:
                endPositions = ButtonPlaceManager.getPositions(parentSize,
                        textInsideCircleButtonRadius, boomButtonBuilders.size(), this);
                break;
            case TextOutsideCircle:
                endPositions = ButtonPlaceManager.getPositions(parentSize,
                        textOutsideCircleButtonWidth, textOutsideCircleButtonHeight,
                        boomButtonBuilders.size(), this);
                break;
            case Ham:
                endPositions = ButtonPlaceManager.getPositions(parentSize,
                        hamButtonWidth, hamButtonHeight,
                        boomButtonBuilders.size(), this);
                break;
        }
        for (int i = 0; i < boomButtons.size(); i++)
            endPositions.get(i).translate(-boomButtons.get(i).centerPoint.getPointX(),
                    -boomButtons.get(i).centerPoint.getPointY());
    }

    /**
     * put BoomButton In Background
     * @param boomButton
     * @param position
     * @return
     */
    private BoomButton putBoomButtonInBackground(BoomButton boomButton, Point position) {
        createBackground();
        boomButton.place(
                position.getPointXToInt(),
                position.getPointYToInt(),
                boomButton.trueWidth(),
                boomButton.trueHeight());
        boomButton.setVisibility(INVISIBLE);
        background.addComponent(boomButton);
        return boomButton;
    }

    /**
     * clearViews
     * @param force
     */
    private void clearViews(boolean force) {
        if (force || !cacheOptimization || inList || inFragment) {
            clearButtons();
            clearBackground();
        }
    }

    /**
     *
     * clear Buttons
     */
    private void clearButtons() {
        needToCreateButtons = true;
        if (background != null) for (BoomButton boomButton : boomButtons) background.removeComponent(boomButton);
        boomButtons.clear();
    }

    /**
     * buttonMaxHeight
     * @return
     */
    private float buttonMaxHeight() {
        switch (buttonEnum) {
            case SimpleCircle: return simpleCircleButtonRadius * 2;
            case TextInsideCircle: return textInsideCircleButtonRadius * 2;
            case TextOutsideCircle: return textOutsideCircleButtonHeight;
            case Ham: return hamButtonHeight;
        }
        return 0;
    }

    /**
     * prevent DragOutside
     */
    private void preventDragOutside() {
        boolean needToAdjustXY = false;
        float newX = getContentPositionX();
        float newY = getContentPositionY();
        ComponentContainer parentView = (ComponentContainer) getComponentParent();

        if (newX < edgeInsetsInParentView.left) {
            newX = edgeInsetsInParentView.left;
            needToAdjustXY = true;
        }

        if (newY < edgeInsetsInParentView.top) {
            newY = edgeInsetsInParentView.top;
            needToAdjustXY = true;
        }

        if (newX > parentView.getWidth() - edgeInsetsInParentView.right - getWidth()) {
            newX = parentView.getWidth() - edgeInsetsInParentView.right - getWidth();
            needToAdjustXY = true;
        }

        if (newY > parentView.getHeight() - edgeInsetsInParentView.bottom - getHeight()) {
            newY = parentView.getHeight() - edgeInsetsInParentView.bottom - getHeight();
            needToAdjustXY = true;
        }
        if (needToAdjustXY) {
//            AnimationManager.animate(this, "pivotX", 0, 300, Ease.getInstance(EaseEnum.EaseOutBack), getContentPositionX(), newX);
//            AnimationManager.animate(this, "pivotY", 0, 300, Ease.getInstance(EaseEnum.EaseOutBack), getContentPositionY(), newY);
//            AnimationManager.animate(this, "contentPositionX", 0, 300, Ease.getInstance(EaseEnum.EaseOutBack), getContentPositionX(), newX);
//            AnimationManager.animate(this, "contentPositionY", 0, 300, Ease.getInstance(EaseEnum.EaseOutBack), getContentPositionY(), newY);
            AnimatorProperty animator = this.createAnimatorProperty();
            animator.setDelay(0);
            animator.setDuration(300);
            animator.setCurve(Ease.getInstance(EaseEnum.EaseOutBack));
            animator.moveFromX(getContentPositionX())
                    .moveToX(newX)
                    .moveFromY(getContentPositionY())
                    .moveToY(newY);
            animator.start();

        }
    }

    /**
     * toLayout
     */
    private void toLayout() {
        if (needToLayout) return;
        needToLayout = true;
        this.postLayout();
    }

    /**
     * delayToDoLayoutJobs
     */
    private void delayToDoLayoutJobs() {
        if (layoutJobsRunnable == null) {
            layoutJobsRunnable = new Runnable() {
                @Override
                public void run() {
                    doLayoutJobs();
                }
            };
        }
        Util.commonEventHandler().postTask(layoutJobsRunnable);
    }

    /**
     * pieceNumber
     * @return
     */
    private int pieceNumber() {
        if (piecePlaceEnum.equals(PiecePlaceEnum.Unknown)) return 0;
        else if (piecePlaceEnum.equals(PiecePlaceEnum.Share)) return boomButtonBuilders.size();
        else if (piecePlaceEnum.equals(PiecePlaceEnum.Custom))
            return customPiecePlacePositions.size();
        else return piecePlaceEnum.pieceNumber();
    }

    /**
     * on Button Click
     * @param index
     * @param boomButton
     */
    @Override
    public void onButtonClick(int index, BoomButton boomButton) {
        if (isAnimating()) return;
        if (onBoomListener != null) onBoomListener.onClicked(index, boomButton);
        if (autoHide) reboom();
    }

    /**
     * place ShareLines View
     */
    private void placeShareLinesView() {
        if (piecePlaceEnum == Share) {
            if (shareLinesView != null) button.removeComponent(shareLinesView);
            shareLinesView = new ShareLinesView(context);
            shareLinesView.setLine1Color(shareLine1Color);
            shareLinesView.setLine2Color(shareLine2Color);
            shareLinesView.setLineWidth(shareLineWidth);
            button.addComponent(shareLinesView);
            shareLinesView.place(0, 0, button.getWidth(), button.getHeight());
        } else {
            if (shareLinesView != null) button.removeComponent(shareLinesView);
        }
    }

    /**
     * set ShareLinesView Data
     */
    private void setShareLinesViewData() {
        if (piecePlaceEnum == Share) shareLinesView.setData(piecePositions, this);
    }

    /**
     * uninitialized BoomButtons
     * @return
     */
    private boolean uninitializedBoomButtons() {
        return buttonEnum.equals(ButtonEnum.Unknown)
                || piecePlaceEnum.equals(PiecePlaceEnum.Unknown)
                || buttonPlaceEnum.equals(ButtonPlaceEnum.Unknown);
    }

    /**
     * isBatterySaveModeTurnOn
     * @return
     */
    private boolean isBatterySaveModeTurnOn() {
        //PowerManager powerManager = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
        //return powerManager.isPowerSaveMode();
        return false;
    }

    /**
     * check AutoBoom
     */
    private void checkAutoBoom() {
        if (autoBoomImmediately) boomImmediately();
        else if (autoBoom) boom();
        autoBoomImmediately = autoBoom = false;
    }

    /**
     * init OrientationListener
     */
    private void initOrientationListener() {
//        if (orientationEventListener == null) {
//            orientationEventListener = new OrientationEventListener(context) {
//                @Override
//                public void onOrientationChanged(int orientation) {
//                    if (orientation != lastOrientation && lastOrientation != OrientationEventListener.ORIENTATION_UNKNOWN) {
//                        isOrientationChanged = true;
//                    }
//                    lastOrientation = orientation;
//                }
//            };
//        }
//        if (orientationEventListener.canDetectOrientation()) {
//            orientationEventListener.enable();
//        }
    }

    /**
     * reLayout After Orientation Changed
     */
    private void reLayoutAfterOrientationChanged() {
        Util.commonEventHandler().postTask(new Runnable() {
            @Override
            public void run() {
                if (background != null) background.reLayout(BoomMenuButton.this);
                calculateStartPositions(true);
                calculateEndPositions();
                switch (boomStateEnum) {
                    case DidReboom:
                        break;
                    case DidBoom:
                        placeButtons();
                        break;
                    case WillBoom:
                    case WillReboom:
                        stopAllAnimations(boomStateEnum == BoomStateEnum.WillBoom);
                        placeButtons();
                        break;
                }
            }
        });
    }

    /**
     * place Buttons
     */
    private void placeButtons() {
        for (int i = 0; i < boomButtons.size(); i++) {
            BoomButton boomButton = boomButtons.get(i);
            Point point = endPositions.get(i);
            boomButton.setContentPositionX(point.getPointX());
            boomButton.setContentPositionY(point.getPointY());
        }
    }

    /**
     * stop All Animations
     * @param isBoomAnimation
     */
    private void stopAllAnimations(boolean isBoomAnimation) {
        if (background != null) background.clearAnimation();
        for (BoomButton boomButton : boomButtons) {
            boomButton.clearAnimation();
        }
    }

    //endregion

    private void ____________________________Builders_and_Buttons() {}

    //region Builders

    /**
     * Add a builder to bmb, notice that @needToLayout will be called.
     *
     * @param builder builder
     */
    public void addBuilder(BoomButtonBuilder builder) {
        boomButtonBuilders.add(builder);
        //toLayout();
    }

    /**
     * Set a builder at index, notice that @needToLayout will be called.
     *
     * @param index index
     * @param builder builder
     */
    public void setBuilder(int index, BoomButtonBuilder builder) {
        boomButtonBuilders.set(index, builder);
        toLayout();
    }

    /**
     * Set builders array, notice that @needToLayout will be called.
     *
     * @param builders builders
     */
    public void setBuilders(ArrayList<BoomButtonBuilder> builders) {
        boomButtonBuilders = builders;
        toLayout();
    }

    /**
     * Get a builder at index.
     *
     * @param index index
     * @return the builder at the index
     */
    public BoomButtonBuilder getBuilder(int index) {
        if (boomButtonBuilders == null || index < 0 || index >= boomButtonBuilders.size()) return null;
        else return boomButtonBuilders.get(index);
    }

    /**
     * Remove a builder, notice that @needToLayout will be called.
     *
     * @param builder builder
     */
    public void removeBuilder(BoomButtonBuilder builder) {
        boomButtonBuilders.remove(builder);
        toLayout();
    }

    /**
     * Remove a builder at index, notice that @needToLayout will be called.
     *
     * @param index index
     */
    public void removeBuilder(int index) {
        boomButtonBuilders.remove(index);
        toLayout();
    }

    /**
     * setEnabled
     * @param enabled
     */
    @Override
    public void setEnabled(boolean enabled) {
        super.setEnabled(enabled);
        button.setEnabled(enabled);
        setButtonBackground();
    }

    /**
     * Set enable attribute of the boom-button at index.
     *
     * @param index index of the boom-button
     * @param enable whether the boom-button should be enable
     */
    public void setEnable(int index, boolean enable) {
        if (index < 0) return;
        if (boomButtonBuilders != null && index < boomButtonBuilders.size()) {
            boomButtonBuilders.get(index).setUnable(!enable);
        }
        if (boomButtons != null && index < boomButtons.size()) {
            boomButtons.get(index).setEnabled(enable);
        }
    }

    /**
     * Remove all builders, notice that @needToLayout will NOT be called.
     */
    public void clearBuilders() {
        boomButtonBuilders.clear();
    }

    /**
     * Get the array of builders.
     *
     * @return array of builders
     */
    public ArrayList<BoomButtonBuilder> getBuilders() {
        return boomButtonBuilders;
    }

    /**
     * Get a boom button at index.
     * Notice that the boom button may be null,
     * because boom buttons are cleared in some situation(in list, in fragment, etc.)
     *
     * @param index index
     * @return boom button
     */
    public BoomButton getBoomButton(int index) {
        if (boomButtons != null && 0 <= index && index < boomButtons.size()) return boomButtons.get(index);
        return null;
    }

    /**
     * Get boom buttons.
     * Notice that the boom button may be null,
     * because boom buttons are cleared in some situation(in list, in fragment, etc.)
     *
     * @return boom buttons
     */
    public ArrayList<BoomButton> getBoomButtons() {
        return boomButtons;
    }

    //endregion

    private void ____________________________Fade_Views() {}

    //region Fade Views

    private void startBoomAnimationForFadeViews(boolean immediately) {
        long duration = immediately ? 1 : showDuration + showDelay * (pieces.size() - 1);
        AnimationManager.animate("alpha", 0, duration,
                new TimelineCurve() {
                    @Override
                    public float getCurvedTime(float input) {
                        return Math.min(input * 2, 1);
                    }
                }, getFadeViews(), 1, 0);
    }

    private void startReboomAnimationForFadeViews(boolean immediately) {
        long duration = immediately ? 1 : hideDuration + hideDelay * (pieces.size() - 1);
        AnimationManager.animate("alpha", 0, duration,
                new TimelineCurve() {
                    @Override
                    public float getCurvedTime(float input) {
                        if (input <= 0.5) return 0;
                        else return Math.min((input - 0.5f) * 2, 1);
                    }
                }, getFadeViews(), 0, 1);
    }

    private ArrayList<Component> getFadeViews() {
        ArrayList<Component> fadeViews = new ArrayList<>();
        for (int i = 0; i < getChildCount(); i++) {
            Component subView = getComponentAt(i);
            if (!(subView == shadow
                    || subView == button
                    || subView == shareLinesView)) fadeViews.add(subView);
        }
        return fadeViews;
    }

    //endregion

    private void ____________________________Getters_and_Setters() {}

    //region Getter and Setter


    /**
     * isCacheOptimization
     * @return
     */
    public boolean isCacheOptimization() {
        return cacheOptimization;
    }

    /**
     * Whether use cache optimization to avoid multi-creating boom-buttons.
     *
     * @param cacheOptimizationIn cache optimization
     */
    public void setCacheOptimization(boolean cacheOptimizationIn) {
        this.cacheOptimization = cacheOptimizationIn;
    }

    /**
     * isBoomInWholeScreen
     * @return
     */
    public boolean isBoomInWholeScreen() {
        return boomInWholeScreen;
    }

    /**
     * Whether the BMB should boom in the whole screen.
     * If this value is false, then BMB will boom its boom-buttons to its parent-view.
     *
     * @param boomInWholeScreenIn boom in the whole screen
     */
    public void setBoomInWholeScreen(boolean boomInWholeScreenIn) {
        this.boomInWholeScreen = boomInWholeScreenIn;
    }

    /**
     * isInList
     * @return
     */
    public boolean isInList() {
        return inList;
    }

    /**
     * When BMB is used in list-view, it must be setInList(true).
     *
     * @param inListIn use BMB in list-view
     */
    public void setInList(boolean inListIn) {
        this.inList = inListIn;
    }

    /**
     * isInFragment
     * @return
     */
    public boolean isInFragment() {
        return inFragment;
    }

    /**
     * When BMB is used in fragment, it must be setInFragment(true).
     *
     * @param inFragmentIn use BMB in fragment
     */
    public void setInFragment(boolean inFragmentIn) {
        this.inFragment = inFragmentIn;
    }

    /**
     * isBackPressListened
     * @return
     */
    public boolean isBackPressListened() {
        return isBackPressListened;
    }

    /**
     * Whether BMB will reboom when the back-key is pressed.
     *
     * @param backPressListened whether BMB will reboom when the back-key is pressed
     */
    public void setBackPressListened(boolean backPressListened) {
        isBackPressListened = backPressListened;
    }

    /**
     * isShadowEffect
     * @return
     */
    public boolean isShadowEffect() {
        return shadowEffect;
    }

    /**
     * Whether BMB should have a shadow-effect.
     * Notice that when you set @backgroundEffect to false, this value will set to false too.
     *
     * @param shadowEffectIn shadow-effect
     */
    public void setShadowEffect(boolean shadowEffectIn) {
        if (this.shadowEffect == shadowEffectIn) return;
        this.shadowEffect = shadowEffectIn;
        initShadow();
    }

    /**
     * get ShadowOffsetX
     * @return
     */
    public int getShadowOffsetX() {
        return shadowOffsetX;
    }

    /**
     * Set the BMB's shadow offset in the x-axis.
     *
     * @param shadowOffsetXIn x-axis shadow offset
     */
    public void setShadowOffsetX(int shadowOffsetXIn) {
        if (this.shadowOffsetX == shadowOffsetXIn) return;
        this.shadowOffsetX = shadowOffsetXIn;
        initShadow();
    }

    /**
     * get ShadowOffsetY
     * @return
     */
    public int getShadowOffsetY() {
        return shadowOffsetY;
    }

    /**
     * Set the BMB's shadow offset in the y-axis.
     *
     * @param shadowOffsetYIn y-axis shadow offset
     */
    public void setShadowOffsetY(int shadowOffsetYIn) {
        if (this.shadowOffsetY == shadowOffsetYIn) return;
        this.shadowOffsetY = shadowOffsetYIn;
        initShadow();
    }

    /**
     * get ShadowRadius
     * @return
     */
    public int getShadowRadius() {
        return shadowRadius;
    }

    /**
     * Set the shadow-radius of BMB, please notice that the "radius" here means the extra
     * radius of BMB.
     * For example, if the radius of BMB is 30dp and the shadow-radius is 5dp, then the
     * radius of shadow-circle behind the BMB if 35dp.
     *
     * @param shadowRadiusIn extra shadow radius
     */
    public void setShadowRadius(int shadowRadiusIn) {
        if (this.shadowRadius == shadowRadiusIn) return;
        this.shadowRadius = shadowRadiusIn;
        initShadow();
    }

    /**
     * get ShadowColor
     * @return
     */
    public Color getShadowColor() {
        return shadowColor;
    }

    /**
     * Set the color of shadow.
     *
     * @param shadowColorIn color of shadow
     */
    public void setShadowColor(Color shadowColorIn) {
        if (this.shadowColor.getValue() == shadowColorIn.getValue()) return;
        this.shadowColor = shadowColorIn;
        initShadow();
    }

    /**
     * get ButtonRadius
     * @return
     */
    public int getButtonRadius() {
        return buttonRadius;
    }

    /**
     * Set the radius of BMB, if you use this method to set the size of BMB,
     * then you should set the width and height of BMB in .xml file to "wrap-content".
     *
     * @param buttonRadiusIn radius of BMB
     */
    public void setButtonRadius(int buttonRadiusIn) {
        if (this.buttonRadius == buttonRadiusIn) return;
        this.buttonRadius = buttonRadiusIn;
        initButton();
        toLayout();
    }

    /**
     * get ButtonEnum
     * @return
     */
    public ButtonEnum getButtonEnum() {
        return buttonEnum;
    }

    /**
     * Set the button-enum for bmb, notice that methods {@link #toLayout()}, {@link #clearPieces()},
     * {@link #clearBuilders()}, and {@link #clearButtons()} will be called.
     *
     * @param buttonEnumIn button-enum
     */
    public void setButtonEnum(ButtonEnum buttonEnumIn) {
        if (this.buttonEnum.equals(buttonEnumIn)) return;
        this.buttonEnum = buttonEnumIn;
        clearPieces();
        clearBuilders();
        clearButtons();
        //toLayout();
    }

    /**
     * isBackgroundEffect
     * @return
     */
    public boolean isBackgroundEffect() {
        return backgroundEffect;
    }

    /**
     * Whether the BMB should have a background effect. Use this when you don't want the
     * circle background of BMB.
     * It can be useful when you want to use BMB in actionbar of in list-view.
     * Please notice that, when BMB does not have a background effect, it does not
     * have shadow effect, either.
     *
     * @param backgroundEffectIn background effect
     */
    public void setBackgroundEffect(boolean backgroundEffectIn) {
        if (this.backgroundEffect == backgroundEffectIn) return;
        this.backgroundEffect = backgroundEffectIn;
        setButtonBackground();
        //toLayout();
    }

    /**
     * isRippleEffect
     * @return
     */
    public boolean isRippleEffect() {
        return rippleEffect;
    }

    /**
     * Whether the BMB should have a ripple-effect.
     * The ripple effect is disable below LOLLIPOP.
     *
     * @param rippleEffectIn ripple effect
     */
    public void setRippleEffect(boolean rippleEffectIn) {
        if (this.rippleEffect == rippleEffectIn) return;
        this.rippleEffect = rippleEffectIn;
        setButtonBackground();
        toLayout();
    }

    /**
     * get NormalColor
     * @return
     */
    public Color getNormalColor() {
        return normalColor;
    }

    /**
     * Set the color of BMB at normal-state.
     *
     * @param normalColorIn the color of BMB at normal-state
     */
    public void setNormalColor(Color normalColorIn) {
        if (this.normalColor.getValue() == normalColorIn.getValue()) return;
        this.normalColor = normalColorIn;
        setButtonBackground();
        toLayout();
    }

    /**
     * get HighlightedColor
     * @return
     */
    public Color getHighlightedColor() {
        return highlightedColor;
    }

    /**
     * Set the color of BMB at highlighted-state.
     *
     * @param highlightedColorIn the color of BMB at highlighted-state
     */
    public void setHighlightedColor(Color highlightedColorIn) {
        if (this.highlightedColor.getValue() == highlightedColorIn.getValue()) return;
        this.highlightedColor = highlightedColorIn;
        setButtonBackground();
        toLayout();
    }

    /**
     * get UnableColor
     * @return
     */
    public Color getUnableColor() {
        return unableColor;
    }

    /**
     * Set the color of BMB at unable-state.
     *
     * @param unableColorIn the color of BMB at unable-state
     */
    public void setUnableColor(Color unableColorIn) {
        if (this.unableColor.getValue() == unableColorIn.getValue()) return;
        this.unableColor = unableColorIn;
        setButtonBackground();
        toLayout();
    }

    /**
     * isDraggable
     * @return
     */
    public boolean isDraggable() {
        return draggable;
    }

    /**
     * Make BMB draggable or not.
     *
     * @param draggableIn draggable or not.
     */
    public void setDraggable(boolean draggableIn) {
        if (this.draggable == draggableIn) return;
        this.draggable = draggableIn;
        initDraggableTouchListener();
    }

    /**
     * get EdgeInsetsInParentView
     * @return
     */
    public Rect getEdgeInsetsInParentView() {
        return edgeInsetsInParentView;
    }

    /**
     * Set the top, left, bottom and right margins in BMB's parent view when BMB is draggable.
     * @param edgeInsetsInParentViewIn the top, left, bottom and right margins
     */
    public void setEdgeInsetsInParentView(Rect edgeInsetsInParentViewIn) {
        if (this.edgeInsetsInParentView.equals(edgeInsetsInParentViewIn)) return;
        this.edgeInsetsInParentView = edgeInsetsInParentViewIn;
        preventDragOutside();
    }

    /**
     * get DotRadius
     * @return
     */
    public float getDotRadius() {
        return dotRadius;
    }

    /**
     * Set the radius of dots in BMB.
     *
     * @param dotRadiusIn radius of dot
     */
    public void setDotRadius(float dotRadiusIn) {
        if (this.dotRadius == dotRadiusIn) return;
        this.dotRadius = dotRadiusIn;
        toLayout();
    }

    /**
     * get HamWidth
     * @return
     */
    public float getHamWidth() {
        return hamWidth;
    }

    /**
     * Set the width of hams in BMB.
     *
     * @param hamWidthIn width of ham
     */
    public void setHamWidth(float hamWidthIn) {
        if (this.hamWidth == hamWidthIn) return;
        this.hamWidth = hamWidthIn;
        toLayout();
    }

    /**
     * get HamHeight
     * @return
     */
    public float getHamHeight() {
        return hamHeight;
    }

    /**
     * Set the height of hams in BMB.
     *
     * @param hamHeightIn height of ham
     */
    public void setHamHeight(int hamHeightIn) {
        if (this.hamHeight == hamHeightIn) return;
        this.hamHeight = hamHeightIn;
        toLayout();
    }

    /**
     * get PieceCornerRadius
     * @return
     */
    public float getPieceCornerRadius() {
        return pieceCornerRadius;
    }

    /**
     * Set the corner radius of pieces.
     *
     * @param pieceCornerRadiusIn corner radius of pieces
     */
    public void setPieceCornerRadius(float pieceCornerRadiusIn) {
        if (this.pieceCornerRadius == pieceCornerRadiusIn) return;
        this.pieceCornerRadius = pieceCornerRadiusIn;
        toLayout();
    }

    /**
     * get PieceHorizontalMargin
     * @return
     */
    public float getPieceHorizontalMargin() {
        return pieceHorizontalMargin;
    }

    /**
     * Set the horizontal margin between pieces(dots, blocks or hams) in BMB.
     *
     * @param pieceHorizontalMarginIn horizontal margin between pieces
     */
    public void setPieceHorizontalMargin(float pieceHorizontalMarginIn) {
        if (this.pieceHorizontalMargin == pieceHorizontalMarginIn) return;
        this.pieceHorizontalMargin = pieceHorizontalMarginIn;
        toLayout();
    }

    /**
     * get PieceVerticalMargin
     * @return
     */
    public float getPieceVerticalMargin() {
        return pieceVerticalMargin;
    }

    /**
     * Set the vertical margin between pieces(dots, blocks or hams) in BMB.
     *
     * @param pieceVerticalMarginIn vertical margin between pieces
     */
    public void setPieceVerticalMargin(float pieceVerticalMarginIn) {
        if (this.pieceVerticalMargin == pieceVerticalMarginIn) return;
        this.pieceVerticalMargin = pieceVerticalMarginIn;
        toLayout();
    }

    /**
     * get PieceInclined Margin
     * @return
     */
    public float getPieceInclinedMargin() {
        return pieceInclinedMargin;
    }

    /**
     * Set the inclined margin between pieces(dots, blocks or hams) in BMB.
     *
     * @param pieceInclinedMarginIn inclined margin between pieces
     */
    public void setPieceInclinedMargin(float pieceInclinedMarginIn) {
        if (this.pieceInclinedMargin == pieceInclinedMarginIn) return;
        this.pieceInclinedMargin = pieceInclinedMarginIn;
        toLayout();
    }

    /**
     * get ShareLine Length
     * @return
     */
    public float getShareLineLength() {
        return shareLineLength;
    }

    /**
     * Set the length of share-lines in BMB, only works when piece-place-enum is Share.
     *
     * @param shareLineLengthIn length of share-lines, in pixel
     */
    public void setShareLineLength(float shareLineLengthIn) {
        if (this.shareLineLength == shareLineLengthIn) return;
        this.shareLineLength = shareLineLengthIn;
        toLayout();
    }

    /**
     * get ShareLine1 Color
     * @return
     */
    public Color getShareLine1Color() {
        return shareLine1Color;
    }

    /**
     * Set the color of share-line 1(the above), only works when piece-place-enum is Share.
     *
     * @param shareLine1ColorIn color of share-line 1
     */
    public void setShareLine1Color(Color shareLine1ColorIn) {
        if (this.shareLine1Color.getValue() == shareLine1ColorIn.getValue()) return;
        this.shareLine1Color = shareLine1ColorIn;
        if (shareLinesView != null) {
            shareLinesView.setLine1Color(shareLine1ColorIn);
            shareLinesView.invalidate();
        }
    }

    /**
     * get ShareLine2 Color
     * @return
     */
    public Color getShareLine2Color() {
        return shareLine2Color;
    }

    /**
     * Set the color of share-line 2(the below), only works when piece-place-enum is Share.
     *
     * @param shareLine2ColorIn color of share-line 2
     */
    public void setShareLine2Color(Color shareLine2ColorIn) {
        if (this.shareLine2Color.getValue() == shareLine2ColorIn.getValue()) return;
        this.shareLine2Color = shareLine2ColorIn;
        if (shareLinesView != null) {
            shareLinesView.setLine2Color(shareLine2ColorIn);
            shareLinesView.invalidate();
        }
    }

    /**
     * get ShareLine Width
     * @return
     */
    public float getShareLineWidth() {
        return shareLineWidth;
    }

    /**
     * Set the width of share-lines in BMB, only works when piece-place-enum is Share.
     *
     * @param shareLineWidthIn width of share-lines, in pixel
     */
    public void setShareLineWidth(float shareLineWidthIn) {
        if (this.shareLineWidth == shareLineWidthIn) return;
        this.shareLineWidth = shareLineWidthIn;
        if (shareLinesView != null) {
            shareLinesView.setLineWidth(shareLineWidthIn);
            shareLinesView.invalidate();
        }
    }

    /**
     * get PiecePlaceEnum
     * @return
     */
    public PiecePlaceEnum getPiecePlaceEnum() {
        return piecePlaceEnum;
    }

    /**
     * Set the piece-place-enum, notice that @requestLayout() will be called.
     *
     * @param piecePlaceEnumIn piece-place-enum
     */
    public void setPiecePlaceEnum(PiecePlaceEnum piecePlaceEnumIn) {
        this.piecePlaceEnum = piecePlaceEnumIn;
        clearPieces();
        //toLayout();
    }

    /**
     * get CustomPiecePlacePositions
     * @return
     */
    public ArrayList<Point> getCustomPiecePlacePositions() {
        return customPiecePlacePositions;
    }

    /**
     * The customized positions of pieces. Only works when the piece-place-enum is **custom**.
     * The elements in positions-array must be Point.
     *
     * @param customPiecePlacePositionsIn customized positions
     */
    public void setCustomPiecePlacePositions(ArrayList<Point> customPiecePlacePositionsIn) {
        if (this.customPiecePlacePositions.equals(customPiecePlacePositionsIn)) return;
        this.customPiecePlacePositions = customPiecePlacePositionsIn;
        clearPieces();
        toLayout();
    }

    /**
     * get OnBoomListener
     * @return
     */
    public OnBoomListener getOnBoomListener() {
        return onBoomListener;
    }

    /**
     * Set the @OnBoomListener of BMB.
     *
     * @param onBoomListenerIn OnBoomListener
     */
    public void setOnBoomListener(OnBoomListener onBoomListenerIn) {
        this.onBoomListener = onBoomListenerIn;
    }

    /**
     * getDimColor
     * @return
     */
    public Color getDimColor() {
        return dimColor;
    }

    /**
     * Set the dim-color of the background when the BMB booms.
     *
     * @param dimColorIn dim-color of background
     */
    public void setDimColor(Color dimColorIn) {
        if (this.dimColor.getValue() == dimColorIn.getValue()) return;
        this.dimColor = dimColorIn;
        ShapeElement element = new ShapeElement();
        element.setRgbColor(RgbColor.fromArgbInt(dimColorIn.getValue()));
        if (boomStateEnum == BoomStateEnum.DidBoom && background != null) background.setBackground(element);
    }

    /**
     * setDelay
     * @param delay
     */
    public void setDelay(long delay) {
        setShowDelay(delay);
        setHideDelay(delay);
    }

    /**
     * setDuration
     * @param duration
     */
    public void setDuration(long duration) {
        setShowDuration(duration);
        setHideDuration(duration);
    }

    /**
     * getShowDelay
     * @return
     */
    public long getShowDelay() {
        return showDelay;
    }

    /**
     * Delay of every boom-button.
     *
     * @param showDelayIn delay of every boom-button
     */
    public void setShowDelay(long showDelayIn) {
        this.showDelay = showDelayIn;
        setShareLinesViewData();
    }

    /**
     * getShowDuration
     * @return
     */
    public long getShowDuration() {
        return showDuration;
    }

    /**
     * Set the duration of every boom-button when booming.
     * Notice that this is not the total duration of the boom animation.
     * For example, if there are 5 boom-buttons, delay of boom animation is 100ms
     * and duration of every boom animation is 1000ms. Then the total duration of
     * all boom animations is 1000ms + 4 * 100ms = 1400ms.
     *
     * @param showDurationIn duration of every boom-button when booming
     */
    public void setShowDuration(long showDurationIn) {
        if (this.showDuration == showDurationIn) return;
        this.showDuration = Math.max(1, showDurationIn);
        setShareLinesViewData();
    }

    /**
     * get HideDelay
     * @return
     */
    public long getHideDelay() {
        return hideDelay;
    }

    /**
     * Delay of every boom-button.
     *
     * @param hideDelayIn delay of every boom-button
     */
    public void setHideDelay(long hideDelayIn) {
        this.hideDelay = hideDelayIn;
        setShareLinesViewData();
    }

    /**
     * get HideDuration
     * @return
     */
    public long getHideDuration() {
        return hideDuration;
    }
    
    /**
     * Set the duration of every boom-button when re-booming.
     * Notice that this is not the total duration of the re-boom animation.
     * For example, if there are 5 boom-buttons, delay of re-boom animation is 100ms
     * and duration of every re-boom animation is 1000ms. Then the total duration of
     * all re-boom animations is 1000ms + 4 * 100ms = 1400ms.
     *
     * @param hideDurationIn duration of every boom-button when booming
     */
    public void setHideDuration(long hideDurationIn) {
        if (this.hideDuration == hideDurationIn) return;
        this.hideDuration = Math.max(1, hideDurationIn);
        setShareLinesViewData();
    }

    /**
     * isCancelable
     * @return
     */
    public boolean isCancelable() {
        return cancelable;
    }

    /**
     * Whether the BMB is cancelable. If the BMB is cancelable, then when after the boom
     * animation of booming, you can re-boom BMB by clicking the background.
     *
     * @param cancelableIn whether the BMB is cancelable
     */
    public void setCancelable(boolean cancelableIn) {
        this.cancelable = cancelableIn;
    }

    /**
     * isAutoHide
     * @return
     */
    public boolean isAutoHide() {
        return autoHide;
    }

    /**
     * Whether the BMB is auto-hide. If the BMB is auto-hide, then when after the boom
     * animation of booming, you can click one of the boom-buttons to re-boom BMB.
     *
     * @param autoHideIn whether the BMB is auto-hide
     */
    public void setAutoHide(boolean autoHideIn) {
        this.autoHide = autoHideIn;
    }

    /**
     * get OrderEnum
     * @return
     */
    public OrderEnum getOrderEnum() {
        return orderEnum;
    }

    /**
     * Set the order-enum for BMB.
     *
     * @param orderEnumIn order-enum
     */
    public void setOrderEnum(OrderEnum orderEnumIn) {
        this.orderEnum = orderEnumIn;
    }

    /**
     * get Frames
     * @return
     */
    public int getFrames() {
        return frames;
    }

    /**
     * Set the animation-frames for every boom-button animation. The higher, the animations
     * more continue.
     *
     * @param framesIn frames for every boom-button animation
     */
    public void setFrames(int framesIn) {
        this.frames = framesIn;
    }

    /**
     * get BoomEnum
     * @return
     */
    public BoomEnum getBoomEnum() {
        return boomEnum;
    }

    /**
     * Set the boom-enum for BMB.
     *
     * @param boomEnumIn boom-enum
     */
    public void setBoomEnum(BoomEnum boomEnumIn) {
        this.boomEnum = boomEnumIn;
    }

    /**
     * set ShowEase Enum
     * @param showEaseEnum
     */
    public void setShowEaseEnum(EaseEnum showEaseEnum) {
        setShowMoveEaseEnum(showEaseEnum);
        setShowScaleEaseEnum(showEaseEnum);
        setShowRotateEaseEnum(showEaseEnum);
    }

    /**
     * get ShowMoveEase Enum
     * @return
     */
    public EaseEnum getShowMoveEaseEnum() {
        return showMoveEaseEnum;
    }

    /**
     * Set the ease type of movement when every boom-button is booming.
     *
     * @param showMoveEaseEnumIn ease type of movement when booming
     */
    public void setShowMoveEaseEnum(EaseEnum showMoveEaseEnumIn) {
        this.showMoveEaseEnum = showMoveEaseEnumIn;
    }

    /**
     * get ShowScaleEase Enum
     * @return
     */
    public EaseEnum getShowScaleEaseEnum() {
        return showScaleEaseEnum;
    }

    /**
     * Set the ease type of scale-animation when every boom-button is booming.
     *
     * @param showScaleEaseEnumIn ease type of scale-animation when booming
     */
    public void setShowScaleEaseEnum(EaseEnum showScaleEaseEnumIn) {
        this.showScaleEaseEnum = showScaleEaseEnumIn;
    }

    /**
     * get ShowRotateEase Enum
     * @return
     */
    public EaseEnum getShowRotateEaseEnum() {
        return showRotateEaseEnum;
    }

    /**
     * Set the ease type of rotate-animation when every boom-button is booming.
     *
     * @param showRotateEaseEnumIn ease type of rotate-animation when booming
     */
    public void setShowRotateEaseEnum(EaseEnum showRotateEaseEnumIn) {
        this.showRotateEaseEnum = showRotateEaseEnumIn;
    }

    /**
     * set HideEase Enum
     * @param hideEaseEnum
     */
    public void setHideEaseEnum(EaseEnum hideEaseEnum) {
        setHideMoveEaseEnum(hideEaseEnum);
        setHideScaleEaseEnum(hideEaseEnum);
        setHideRotateEaseEnum(hideEaseEnum);
    }

    /**
     * get Hide MoveEase Enum
     * @return
     */
    public EaseEnum getHideMoveEaseEnum() {
        return hideMoveEaseEnum;
    }

    /**
     * Set the ease type of movement when every boom-button is re-booming.
     *
     * @param hideMoveEaseEnumIn ease type of movement when re-booming
     */
    public void setHideMoveEaseEnum(EaseEnum hideMoveEaseEnumIn) {
        this.hideMoveEaseEnum = hideMoveEaseEnumIn;
    }

    /**
     * get HideScaleEase Enum
     * @return
     */
    public EaseEnum getHideScaleEaseEnum() {
        return hideScaleEaseEnum;
    }

    /**
     * Set the ease type of scale-animation when every boom-button is re-booming.
     *
     * @param hideScaleEaseEnumIn ease type of scale-animation when re-booming
     */
    public void setHideScaleEaseEnum(EaseEnum hideScaleEaseEnumIn) {
        this.hideScaleEaseEnum = hideScaleEaseEnumIn;
    }

    /**
     * get Hide RotateEase Enum
     * @return
     */
    public EaseEnum getHideRotateEaseEnum() {
        return hideRotateEaseEnum;
    }

    /**
     * Set the ease type of rotate-animation when every boom-button is re-booming.
     *
     * @param hideRotateEaseEnumIn ease type of rotate-animation when re-booming
     */
    public void setHideRotateEaseEnum(EaseEnum hideRotateEaseEnumIn) {
        this.hideRotateEaseEnum = hideRotateEaseEnumIn;
    }

    /**
     * get RotateDegree
     * @return
     */
    public int getRotateDegree() {
        return rotateDegree;
    }

    /**
     * Whether there are 3d animations for boom-buttons.
     *
     * @param use3DTransformAnimationIn Whether there are 3d animations for boom-buttons.
     */
    public void setUse3DTransformAnimation(boolean use3DTransformAnimationIn) {
        this.use3DTransformAnimation = use3DTransformAnimationIn;
    }

    /**
     * is Use 3D Transform Animation
     * @return
     */
    public boolean isUse3DTransformAnimation() {
        return use3DTransformAnimation;
    }

    /**
     * Whether BMB will boom automatically when it's shown.
     * This property can be useful if the BMB is supposed to boom when its activity appears.
     *
     * @param autoBoomIn true/false
     */
    public void setAutoBoom(boolean autoBoomIn) {
        this.autoBoom = autoBoomIn;
    }

    /**
     * isAutoBoom
     * @return
     */
    public boolean isAutoBoom() {
        return autoBoom;
    }

    /**
     * Whether BMB will boom automatically without animations when it's shown.
     * This property can be useful if the BMB is supposed to boom when its activity appears.
     *
     * @param autoBoomImmediatelyIn true/false
     */
    public void setAutoBoomImmediately(boolean autoBoomImmediatelyIn) {
        this.autoBoomImmediately = autoBoomImmediatelyIn;
    }

    /**
     * is AutoBoom Immediately
     * @return
     */
    public boolean isAutoBoomImmediately() {
        return autoBoomImmediately;
    }

    /**
     * Set the rotate degree of rotate-animation of every boom-button.
     *
     * @param rotateDegreeIn rotate degree
     */
    public void setRotateDegree(int rotateDegreeIn) {
        this.rotateDegree = rotateDegreeIn;
    }

    /**
     * get ButtonPlace Enum
     * @return
     */
    public ButtonPlaceEnum getButtonPlaceEnum() {
        return buttonPlaceEnum;
    }

    /**
     * Set the button-place-enum.
     *
     * @param buttonPlaceEnumIn button-place-enum
     */
    public void setButtonPlaceEnum(ButtonPlaceEnum buttonPlaceEnumIn) {
        this.buttonPlaceEnum = buttonPlaceEnumIn;
        clearButtons();
        needToCalculateStartPositions = true;
    }

    /**
     * get CustomButton PlacePositions
     * @return
     */
    public ArrayList<Point> getCustomButtonPlacePositions() {
        return customButtonPlacePositions;
    }

    /**
     * set CustomButton PlacePositions
     * @param customButtonPlacePositionsIn
     */
    public void setCustomButtonPlacePositions(ArrayList<Point> customButtonPlacePositionsIn) {
        this.customButtonPlacePositions = customButtonPlacePositionsIn;
        clearButtons();
        needToCalculateStartPositions = true;
    }

    /**
     * get Button PlaceAlignment Enum
     * @return
     */
    public ButtonPlaceAlignmentEnum getButtonPlaceAlignmentEnum() {
        return buttonPlaceAlignmentEnum;
    }

    /**
     * Set the button-place-alignment-enum.
     *
     * @param buttonPlaceAlignmentEnumIn button-place-alignment-enum
     */
    public void setButtonPlaceAlignmentEnum(ButtonPlaceAlignmentEnum buttonPlaceAlignmentEnumIn) {
        this.buttonPlaceAlignmentEnum = buttonPlaceAlignmentEnumIn;
    }

    /**
     * get Button HorizontalMargin
     * @return
     */
    public float getButtonHorizontalMargin() {
        return buttonHorizontalMargin;
    }

    /**
     * Set the horizontal-margin between buttons.
     *
     * @param buttonHorizontalMarginIn horizontal-margin
     */
    public void setButtonHorizontalMargin(float buttonHorizontalMarginIn) {
        this.buttonHorizontalMargin = buttonHorizontalMarginIn;
    }

    /**
     * get Button VerticalMargin
     * @return
     */
    public float getButtonVerticalMargin() {
        return buttonVerticalMargin;
    }

    /**
     * Set the vertical-margin between buttons.
     *
     * @param buttonVerticalMarginIn vertical-margin
     */
    public void setButtonVerticalMargin(float buttonVerticalMarginIn) {
        this.buttonVerticalMargin = buttonVerticalMarginIn;
    }

    /**
     * get Button InclinedMargin
     * @return
     */
    public float getButtonInclinedMargin() {
        return buttonInclinedMargin;
    }

    /**
     * Set the inclined-margin between buttons.
     *
     * @param buttonInclinedMarginIn Inclined-margin
     */
    public void setButtonInclinedMargin(float buttonInclinedMarginIn) {
        this.buttonInclinedMargin = buttonInclinedMarginIn;
    }

    /**
     * get Button TopMargin
     * @return
     */
    public float getButtonTopMargin() {
        return buttonTopMargin;
    }

    /**
     * Set the top-alignment margin between screen and buttons.
     *
     * @param buttonTopMarginIn Top-alignment margin
     */
    public void setButtonTopMargin(float buttonTopMarginIn) {
        this.buttonTopMargin = buttonTopMarginIn;
    }

    /**
     * get Button BottomMargin
     * @return
     */
    public float getButtonBottomMargin() {
        return buttonBottomMargin;
    }

    /**
     * Set the bottom-alignment margin between screen and buttons.
     *
     * @param buttonBottomMarginIn Bottom-alignment margin
     */
    public void setButtonBottomMargin(float buttonBottomMarginIn) {
        this.buttonBottomMargin = buttonBottomMarginIn;
    }

    /**
     * get Button LeftMargin
     * @return
     */
    public float getButtonLeftMargin() {
        return buttonLeftMargin;
    }

    /**
     * Set the left-alignment margin between screen and buttons.
     *
     * @param buttonLeftMarginIn Left-alignment margin
     */
    public void setButtonLeftMargin(float buttonLeftMarginIn) {
        this.buttonLeftMargin = buttonLeftMarginIn;
    }

    /**
     * get Button RightMargin
     * @return
     */
    public float getButtonRightMargin() {
        return buttonRightMargin;
    }

    /**
     * Set the right-alignment margin between screen and buttons.
     *
     * @param buttonRightMarginIn Right-alignment margin
     */
    public void setButtonRightMargin(float buttonRightMarginIn) {
        this.buttonRightMargin = buttonRightMarginIn;
    }

    /**
     * get Bottom HamButton TopMargin
     * @return
     */
    public float getBottomHamButtonTopMargin() {
        return bottomHamButtonTopMargin;
    }

    /**
     * Set the top-margin of bottom ham-boom-button. This method is used when the bottom
     * ham-boom-button has different meaning compared with others. For example, the bottom
     * ham-boom-button maybe used as a "cancel" selection.
     *
     * @param bottomHamButtonTopMarginIn top-margin of bottom ham-boom-button
     */
    public void setBottomHamButtonTopMargin(float bottomHamButtonTopMarginIn) {
        this.bottomHamButtonTopMargin = bottomHamButtonTopMarginIn;
    }

    /**
     * isOrientationAdaptable
     * @return
     */
    public boolean isOrientationAdaptable() {
        return isOrientationAdaptable;
    }

    /**
     * set Orientation Changed
     * @param orientation
     */
    public void setOrientationChanged(DisplayOrientation orientation)
    {
        if (orientation != lastOrientation && orientation != DisplayOrientation.UNSPECIFIED) {
            // disable the feature because the task manager will kill the current ability when orientation changed.
            //isOrientationChanged = true;
        }
        lastOrientation = orientation;
    }

    /**
     * Usually, you don't need to worry about the positions of sub-buttons of BMB when the screen
     * orientation is changed. Because the activity will be destroyed and re-created. But if the
     * "configChanges" property of activity is set, then you need to set orientation-adaptable to
     * true, then BMB will change the positions of sub-buttons when orientation of screen is
     * changed.
     *
     * @param orientationAdaptable orientationAdaptable
     */
    public void setOrientationAdaptable(boolean orientationAdaptable) {
        isOrientationAdaptable = orientationAdaptable;
        if (isOrientationAdaptable) {
            initOrientationListener();
        }
    }

    /**
     * set Parent Component
     * @param cc
     */
    public void setParentComponent(ComponentContainer cc) {
        rootContainer = cc;
    }
    //endregion
}
